生成器(generator)
在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。生成器本身就是迭代器。
1 生成器表达式
生成器表达式的语法和列表生成式的一样,只不过把列表生成式的[]换成()。
gen=(x+y for x in ("A","B","C") for y in ("a","b","c"))
print(gen)
print(next(gen))
print(next(gen))
result:
<generator object generator at 0x00000203D50FF468>
Aa
Ab
2 生成器函数
在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。
yield的作用:
- yield把函数变成生成器(把__iter__()和__next__()封装到函数里)
- yield能让函数挂起,并保存函数当前的运行状态。
- yield能返回值(这一点和return类型)
yield和return的区别:
return返回一次值,函数就结束运行。而yield能多次返回值。
- 有了yield这样灵活的语法,我们就能自己定义生成器了。
from collections import Iterator
def func(n):
i=0
while i<=n:
yield i
i+=2
g=func(10)
if isinstance(g,Iterator):
print(next(g),end=" ")
print(next(g),end=" ")
print(next(g),end=" ")
print(next(g),end=" ")
print(next(g),end=" ")
print(next(g),end=" ")
print(next(g),end=" ")
else:
print(g)
result:
Traceback (most recent call last):
0 2 4 6 8 10 File "D:/devtools/workspace/python/PycharmProjects/py_fulstack_s4/day23/yield用法.py", line 19, in <module>
print(next(g),end=" ")
StopIteration
每次运行遇到yield先执行再挂起,并可以通过next()方法来获取其返回值,然后继续向下运行,直到遇到下一个yield挂起并返回。直到遇到StopIteration停止。
改用for遍历:
if isinstance(g,Iterator):
for i in g:
print(i, end=" ")
else:
print(g)
- 每一次调用生成器函数返回一个生成器对象,调用同一个生成器函数返回的多个生成器对象互相之间不影响。
def func():
pass
a=func()
b=func()
print(a is b) #True
def generator():
yield 1
g=generator
print(g) #<function generator at 0x0000020A119E3EA0>
g1=generator()
print(g1) #<generator object generator at 0x0000020A11E2F410>
g2=generator()
print(g2) #<generator object generator at 0x0000020A11E2F1A8>
print(g1 is g2) #False
g是函数生成器的地址,而g1和g2是生成器对象的地址。可以看出同一个函数生成器调用后赋值给多个变量,这些变量是互不受影响的。
- 如何接收另一个生成器函数的迭代。通过from关键字来实现
def counter(start=0):
while True:
yield start
start += 1
def gen():
yield 1
yield 2
yield from counter(10)
g=gen()
for i in range(10):
print(next(g),end=" ")
结果:
1 2 10 11 12 13 14 15 16 17
3 生成器常用的方法
- close() 关闭生成器,关闭以后再调用next()方法会报出StopIteration。
li=(x for x in range(10) if x%2==0)
print(next(li))
print(next(li))
li.close()
print(next(li))
result:
Traceback (most recent call last):
File "D:/devtools/workspace/python/PycharmProjects/py_fulstack_s4/day23/yield用法.py", line 14, in <module>
print(next(li))
StopIteration
0
2
- throws()用来向生成器函数送入一个异常,可以结束系统定义的异常,或者自定义的异常。
throw()后直接抛出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。
def gen():
while True:
try:
yield "value 1"
yield "value 2"
print("here!")
except ValueError:
print("the ValueError is here")
except TypeError:
break
g=gen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))
result:
value 1
the ValueError is here
Traceback (most recent call last):
value 1
value 2
File "D:/devtools/workspace/python/PycharmProjects/py_fulstack_s4/day23/gen.py", line 18, in <module>
print(g.throw(TypeError))
StopIteration
执行过程:
- print(next(g)):输出value 1,并挂起。
- 由于执行了g.throw(ValueError),所以会跳过所有后续的try语句,也就是说yield ‘value 2’不会被执行,然后进入到except语句,打印出the ValueError is here。然后再次进入到while语句部分,消耗一个yield,所以会输出value 1。
- print(next(g)),会执行yield ‘alue 2’语句,并挂起。
- g.throw(TypeError):会跳出try语句,从而print(‘here!’)不会被执行,然后执行break语句,跳出while循环,然后到达程序结尾,所以跑出StopIteration异常。
5 yeild在协程中的使用
send()接受外部传入的一个变量,并根据变量内容计算结果后返回接受外部传入的一个变量,并根据变量内容计算结果后返回
def func(name):
print("%s 开始点餐!"%(name))
food_menu=[]
while True:
food=yield food_menu
food_menu.append(food)
print("%s 的菜单是:%s"%(name,food_menu))
print("完成点餐!")
g=func("egg")
print(next(g))
print(g.send("西红柿炒番茄"))
print(g.send("白菜炒包菜"))
result:
egg 开始点餐!
[]
egg 的菜单是:['西红柿炒番茄']
['西红柿炒番茄']
egg 的菜单是:['西红柿炒番茄', '白菜炒包菜']
['西红柿炒番茄', '白菜炒包菜']
- g.send(None)或者next(g)可以启动生成器函数并执行到第一个yield语句结束的位置。并执行到第一个yield语句结束的位置。此时food_menu没有值,所以返回为[]。如果去掉next(g),程序会报错TypeError: can't send non-None value to a just-started generator。
- 当运行代码g.send("西红柿炒番茄"),实际上是把"西红柿炒番茄"赋值给food,而g.send()的返回值就是yield后面跟着的food_menu。输出结果:['西红柿炒番茄']
- 重复第2步,输出:['西红柿炒番茄', '白菜炒包菜']。
其他生成器链接:http://www.cnblogs.com/huxi/archive/2011/07/14/2106863.html