笔记
yield是一个生成器关键字,类比return,用来处理无限序列的返回值。这是我之前对yield的全部理解。
换言之,我对yield只有一个浅显的认知,,用它来代替return 的函数,会自动变成一个生成器函数,在轮循的时候不断调用,如:
In [1]: def simple_generator_function():
...: yield 1
...: yield 2
...: yield 3
...:
In [2]: simple_generator_function() #可以看到该函数定义了一个生成器的对象
Out[2]: <generator object simple_generator_function at 0x7fa90d48ee60>
In [3]: for value in simple_generator_function():
...: print(value)
...:
1
2
3
for循环这个简单的生成器,可以每轮读一个值。
也可以用next调用生成器,生成下一个值,如:
In [4]: our_generator = simple_generator_function()
In [5]: next(our_generator)
Out[5]: 1
In [6]: next(our_generator)
Out[6]: 2
In [7]: next(our_generator)
Out[7]: 3
In [8]: next(our_generator)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-8-57a7077cf65e> in <module>()
----> 1 next(our_generator)
StopIteration:
这里也可以看到,函数内置了__next__()用法,但是当把生成器中所有值全部生成之后,会抛出一个异常。每个生成器只能使用一次,所以想要再用只能再创建一个。
最近写爬虫用到,所以仔细看了一下特性,yield有个冻结层的功能,第一轮next()取出一个值的同时,函数停留在本行不往下进行,指导一下轮查询的到来,例如:
In [35]: def myFun(number):
...: while True:
...: if number < 15:
...: print("yield开始前")
...: yield number
...: print("yield开始后")
...: number += 1
...:
In [36]: generator = myFun(0)
In [37]: next(generator)
yield开始前
Out[37]: 0
In [38]: next(generator)
yield开始后
yield开始前
Out[38]: 1
In [39]: next(generator)
yield开始后
yield开始前
Out[39]: 2
可以看到,每次的结束log都是在下一轮next下执行的。
还有一点,值传递时用next会传出None,一段代码就全明白了
In [50]: def get_primes(number):
...: while True:
...: if number < 15:
...: print(type(number))
...: number = yield number
...: print(number)
...: number += 1
...:
In [51]: generator = get_primes(2)
In [52]: next(generator)
<class 'int'>
Out[52]: 2
In [53]: next(generator) # 很显然,因为第一轮的复制,第二轮报错了
None
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-53-323ce5d717bb> in <module>()
----> 1 next(generator)
<ipython-input-50-922d08ae3a11> in get_primes(number)
5 number = yield number
6 print(number)
----> 7 number += 1
8
TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int'
工作中send()也很常用,我看到一篇帖子,利用一个简易的生产-消费模型很清楚的表示了send()的用法,是这样的:
import random
def get_data():
"""返回0到9之间的3个随机数"""
return random.sample(range(10), 3)
def consume():
"""显示每次传入的整数列表的动态平均值"""
running_sum = 0
data_items_seen = 0
while True:
data = yield
data_items_seen += len(data)
running_sum += sum(data)
print('The running average is {}'.format(running_sum / float(data_items_seen)))
def produce(consumer):
"""产生序列集合,传递给消费函数(consumer)"""
while True:
data = get_data()
print('Produced {}'.format(data))
consumer.send(data)
yield
if __name__ == '__main__':
consumer = consume()
consumer.send(None)
producer = produce(consumer)
for _ in range(10):
print('Producing...')
next(producer)
可以看到,代码先对生成器send了一个None,来启动生成器,后面,每次传递生成的data