语言基础之:生成器Generator
生成器Generator
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
-
只有一个I.next()方法
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。 -
只记录当前位置:
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。 -
调用一个生成器函数,返回的是一个迭代器对象。
1 列表生成式
列表生成式在调用列表之前创建完整的列表,如果列表参数庞大,会占用大量的内存空间。
>>> L = [x*2 for x in range(10)]
>>> L
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> L[3]
6
>>> len(L)
10
>>>
2 生成器
generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
>>> I = (i*2 for i in range(10000))
>>> print(I)
<generator object <genexpr> at 0x0000021DB16747B0>
# 创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
>>> for value in I:
... print(value)
0
...
11060
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
KeyboardInterrupt
>>> I[100]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'generator' object is not subscriptable
>>> I.__next__()
11062
>>> I.__next__()
11064
>>> I.__next__()
11066
>>> I.__next__()
11068
>>> I.__next__()
11070
>>>
2.1 斐波拉契数列(Fibonacci)
# Description:打印斐波拉契数列(Fibonacci):
# 除第一个和第二个数外,任意一个数都可由前两个数相加得到:
# 1, 1, 2, 3, 5, 8, 13, 21, 34, ...
# 斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
def fib(max):
x, y = 0, 1
for i in range(max):
print(y)
x, y = y, x + y
max = int(input('max:'))
fib(max)
注意,赋值语句:
x, y = y, x + y
相当于:
z = (y, x + y) # z是一个tuple
x = z[0]
y = z[1]
# 结论:不必显式写出临时变量z就可以赋值。
2.2 yield
- 如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator
def fib(max):
x, y = 0, 1
for i in range(max):
# print(y)
yield y
x, y = y, x + y
# return 'done'
f = fib(10)
print(f)
# <generator object fib at 0x000001E79A57CAC0>
print(f.__next__())
print(f.__next__())
print('-----out-----')
print(f.__next__())
print(f.__next__())
print('-----start loop-----')
for i in f:
print(i)
# 输出:
1
1
-----out-----
2
3
-----start loop-----
5
8
13
21
34
55
2.2.1 yield解析
def Foo():
print('starting eating baozi...')
while True:
baozi = yield 'return_code'
print('\033[34;1m c\033[0m is eating baozi %s'% baozi)
if __name__ == '__main__':
f = Foo()
# print(next(f))
'''
输出结果:
starting eating baozi...
None
'''
# 单步输出
print('step 1 :', f.__next__())
print('*'*25)
print('step 2 :', f.__next__())
print('*'*25)
print('step 3 :', f.send(3))
'''
输出结果:
starting eating baozi...
step 1 : return_code
*************************
c is eating baozi None
step 2 : return_code
*************************
c is eating baozi 3
step 3 : return_code
'''
- 程序执行后,会得到一个生成器f,f = {generator} <generator object Foo at 0x0000028EFD744DD0>
- 调用f__next__()指针下移一位,执行Foo函数,打印’starting eating baozi…’;再进入while循环,执行’yield’语句,会暂停Foo函数执行、返回’return_code’并跳出此函数,此时由于程序已经跳出此函数,变量’baozi’并没有被赋值。
- 执行print(’*’*25)
- 调用f.__next__()指针下移一位,Foo函数将从上个暂停的指针位置执行函数,'baozi’没有被赋值,返回None;由于while循环,执行’yield’语句,会暂停Foo函数执行、返回’return_code’并跳出此函数
- 执行print(’*’*25)
- 调用f.send(3),由于send()方法的作用是恢复generator并发送一个值给当前yield表达式。Foo函数将从上个暂停的指针位置执行函数,send()将3赋值给’baozi’变量,返回’3’,由于while循环,再执行’yield’语句,会暂停Foo函数执行、返回’return_code’并跳出此函数
2.2.2 抓取生成器返回值
def fib(max):
x, y = 0, 1
for i in range(max):
# print(y)
yield y
x, y = y, x + y
return 'done'
g = fib(5)
while True:
try:
z = next(g)
print('z:', z)
except StopIteration as e:
print('Generator return value:', e.value)
break
# 输出
z: 1
z: 1
z: 2
z: 3
z: 5
Generator return value: done
2.2.3 通过yield实现在单线程的情况下实现并发运算的效果
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield
print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
b1 = 'big baozi'
c = consumer('abc')
c.__next__()
c.__next__()
c.send(b1)
c.__next__()
# 输出
abc 准备吃包子啦!
包子[None]来了,被[abc]吃了!
包子[big baozi]来了,被[abc]吃了!
包子[None]来了,被[abc]吃了!
结论:
- yield : 保存当前状态,返回
- next()调用yield但不会给yield传值
- send()调用yield会给yield传值