资讯专栏INFORMATION COLUMN

python中的yield使用详解

CntChen / 2511人阅读

摘要:是的一个关键字,刚接触的时候对这个关键字一知半解,掌握之后才发现这关键字有大用,本文将对的使用方法好好梳理一番。使用创建生成器在中,生成器是一种可迭代对象,但可迭代对象不一定是生成器。

yield是python的一个关键字,刚接触python的时候对这个关键字一知半解,掌握之后才发现这关键字有大用,本文将对yield的使用方法好好梳理一番。

1 使用yield创建生成器

在python中,生成器是一种可迭代对象,但可迭代对象不一定是生成器。
例如,list就是一个可迭代对象

>>> a = list(range(3))
>>> for i in a:
    print(i)

0
1
2
3

但是一个list对象所有的值都是放在内存中的,如果数据量非常大的话,内存就有可能不够用;这种情况下,就可以生成器,例如,python可以用“()”构建生成器对象:

>>> b = (x for x in range(3))
>>> for i in b:
    print(i)

0
1
2
>>> for i in b:
    print(i)
    
>>> 

生成器可以迭代的,并且数据实时生成,不会全部保存在内存中;值得注意的是,生成器只能读取一次,从上面的运行结果可以看到,第二次for循环输出的结果为空

在实际编程中,如果一个函数需要产生一段序列化的数据,最简单的方法是将所有结果都放在一个list里返回,如果数据量很大的话,应该考虑用生成器来改写直接返回列表的函数(Effective Python, Item 16).

>>> def get_generator():
    for i in range(3):
        print("gen ", i)
        yield i
        
>>> c = get_generator()    
>>> c = get_generator()
>>> for i in c:
    print(i)
    
gen  0
0
gen  1
1
gen  2
2

由上面的代码可以看出,当调用get_generator函数时,并不会执行函数内部的代码,而是返回了一个迭代器对象,在用for循环进行迭代的时候,函数中的代码才会被执行。
除了使用for循环获得生成器返回的值,还可以使用next和send

>>> c = get_generator()
>>> print(next(c))
gen  0
0
>>> print(next(c))
gen  1
1
>>> print(next(c))
gen  2
2
>>> print(next(c))
Traceback (most recent call last):
  File "", line 1, in 
    print(next(c))
StopIteration
>>> c = get_generator()
>>> c.send(None)
gen  0
0
>>> c.send(None)
gen  1
1
>>> c.send(None)
gen  2
2
>>> c.send(None)
Traceback (most recent call last):
  File "", line 1, in 
    c.send(None)
StopIteration

生成器的结果读取完后,会产生一个StopIteration的异常

2 coroutines中使用

yield一个常见的使用场景是通过yield来实现协程,已下面这个生产者消费者模型为例:

def consumer():
    r = "yield"
    while True:
        print("[CONSUMER] r is %s..." % r)
        #当下边语句执行时,先执行yield r,然后consumer暂停,此时赋值运算还未进行
        #等到producer调用send()时,send()的参数作为yield r表达式的值赋给等号左边
        n = yield r #yield表达式可以接收send()发出的参数
        if not n:
            return # 这里会raise一个StopIteration
        print("[CONSUMER] Consuming %s..." % n)
        r = "200 OK"

def produce(c):
    c.send(None)
    n = 0
    while n < 5:
        n = n + 1
        print("[PRODUCER] Producing %s..." % n)
        r = c.send(n)   #调用consumer生成器
        print("[PRODUCER] Consumer return: %s" % r)
    c.send(None)    
    c.close()

c = consumer()
produce(c)
[CONSUMER] r is yield...
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[CONSUMER] r is 200 OK...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[CONSUMER] r is 200 OK...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 3...
[CONSUMER] Consuming 3...
[CONSUMER] r is 200 OK...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 4...
[CONSUMER] Consuming 4...
[CONSUMER] r is 200 OK...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 5...
[CONSUMER] Consuming 5...
[CONSUMER] r is 200 OK...
[PRODUCER] Consumer return: 200 OK
Traceback (most recent call last):
  File ".foobar.py", line 51, in 
    produce(c)
  File ".foobar.py", line 47, in produce
    c.send(None)
StopIteration

在上面的例子中可以看到,yield表达式与send配合,可以起到交换数据的效果,

n = yield r

r = c.send(n)
3 contextmanager中使用

另外一个比较有意思的使用场景是在contextmanager中,如下:

import logging
import contextlib

def foobar():
    logging.debug("Some debug data")
    logging.error("Some error data")
    logging.debug("More debug data")


@contextlib.contextmanager
def debug_logging(level):
    logger = logging.getLogger()
    old_level = logger.getEffectiveLevel()
    logger.setLevel(level)
    try:
        yield #这里表示with块中的语句
    finally:
        logger.setLevel(old_level)


with debug_logging(logging.DEBUG):
    print("inside context")
    foobar()
print("outside context")
foobar()
inside context
DEBUG:root:Some debug data
ERROR:root:Some error data
DEBUG:root:More debug data
outside context
ERROR:root:Some error data

在上面的代码中,通过使用上下文管理器(contextmanager)来临时提升了日志的等级,yield表示with块中的语句;

总结

yield表达式可以创建生成器,应该考虑使用生成器来改写直接返回list的函数;

由于生成器只能读取一次,因此使用for循环遍历的时候要格外注意;生成器读取完后继续读的话会raise一个StopIteration的异常,实际编程中可以使用这个异常来作为读取终止的判断依据;

yield一个常见的使用场景是实现协程;通过与send函数的配合,可以起到交换数据的效果;

yield还可以在contextmanager修饰的函数中表示with块中的语句;

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42808.html

相关文章

  • pythonyield的用法详解——最简单,最清晰的解释

    摘要:程序执行,程序会从关键字那一行继续向下运行,会把这个值赋值给变量由于方法中包含方法,所以程序会继续向下运行执行方法,然后再次进入循环程序执行再次遇到关键字,会返回后面的值后,程序再次暂停,直到再次调用方法或方法。 此文转载,侵删,原文地址:https://blog.csdn.net/mieleiz... 首先,如果你还没有对yield有个初步分认识,那么你先把yield看做return...

    VPointer 评论0 收藏0
  • 分布式计算框架MapReduce

    1 MapReduce概念 和 MapReduce编程模型什么是MapReduce源于Google的MapReduce论文(2004年12月)Hadoop的MapReduce是Google论文的开源实现MapReduce优点: 海量数据离线处理&易开发MapReduce缺点: 实时流式计算MapReduce分而治之的思想数钱实例:一堆钞票,各种面值分别是多少单点策略一个人数所有的钞票,数出各种面值...

    Tecode 评论0 收藏0
  • Python装饰器的另类用法

    摘要:今天我们一起探讨一下装饰器的另类用法。语法回顾开始之前我们再将装饰器的语法回顾一下。例子本身只是演示了装饰器的一种用法,但不是推荐你就这样使用装饰器。类装饰器在以前,还不支持类装饰器。 之前有比较系统介绍过Python的装饰器(请查阅《详解Python装饰器》),本文算是一个补充。今天我们一起探讨一下装饰器的另类用法。 语法回顾 开始之前我们再将Python装饰器的语法回顾一下。 @d...

    hqman 评论0 收藏0
  • redux-saga框架使用详解及Demo教程

    摘要:通过创建将所有的异步操作逻辑收集在一个地方集中处理,可以用来代替中间件。 redux-saga框架使用详解及Demo教程 前面我们讲解过redux框架和dva框架的基本使用,因为dva框架中effects模块设计到了redux-saga中的知识点,可能有的同学们会用dva框架,但是对redux-saga又不是很熟悉,今天我们就来简单的讲解下saga框架的主要API和如何配合redux框...

    Nosee 评论0 收藏0

发表评论

0条评论

CntChen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<