生成器
什么是生成器?
生成器是一种数据类型,它自动实现了迭代器协议(也就是说不用iter
把它变成一个迭代器,它本身就能当作迭代器用,本身就能被next
,本身就有__next__()
方法)。所以,它在某种程度上可以认为是一个迭代器。不过它的功能要比迭代器更强大。因为它可以用来保存函数的状态。它的特点是生内存且效率高。
如何定义一个生成器?
两种形式可以做到这一点:
- 生成器函数
- 生成器表达式
在具体说生成器之前,先来说说三元表达式和列表推导式。
python这么NB的语言也要像C那样有3元表达式,不然就太Low了。
res = 'bjfu' if name == 'vth' else 'pku'
这个等价于C语言中的
name == 'vth'?'bjfu':'pku'
再来说说列表推导式:
列表推导式能够在不产生额外开销的情况下生成一个列表,比for
循环生成列表要好用。
l = [x for x in range(5)]
l = [i for i in range(100) for j in range(20) if i == j]
l1 = [i for i in range(100) if i != 1 for j in range(20) if i == j]
print(l)
print(l1)
列表推导式可以用多个 for if
语句,但是不能出现else
语句。
说完了,现在来说一说生成器表达式:
生成器表达式的形式是怎样的?
将列表推导式的[]
换成()
即可。
max,min,sum,sort,for
等都是基于迭代器协议工作的,也就是说,他们可以处理可迭代对象,迭代器,生成器。都没问题。
再来说说生成器函数:
将普通函数中的return
语句改成yield
语句,这样就把一个普通函数变成了一个生成器函数。这个函数就是一个生成器。
def test():
yield
print(test())
输出:
<generator object test at 0x00000209A0A91A98>
生成器的优点:
延迟计算(惰性计算),不会像列表推导式那样一次生成一个大列表,这个是next
一次就给一个,比较省内存,在处理大规模数据的时候特别有用。
有效提高代码可读性。
生成器特点:
语法和函数类似,只不过是将return
换成了yield
。注意return
和 yield
可以在同一个函数中共存,但是写return
并没有什么意义。因为只要函数体中出现了yield
,这个函数就变成了一个生成器,要return
也没用。
自动实现了迭代器协议
只能循环遍历一次(像人的年龄一样,开弓没有回头箭),当一个生成器遍历到抛出StopIteration
异常时,这个生成器就结束了。
生成器之中有一个send()
方法,send()
方法的工作流程是这样的:
没时间看图的来看字:
send
和next
差不多,只不过send传的参数交给了上一次的yield
作为yield
表达式的值可以被某变量存储,send
还自动启动下一次的yield
,下一次yield
表达式中的yield
返回值交给send
作为send
方法的返回值。
下面写一个程序单线程模拟并发效果:
'''
程序思路:
producer靠send生产包子给consumer中的yield返回吃。
'''
from time import sleep
def consumer(name):
print('start')
while True:
baozi = yield # 接收procuder产生的包子 send发送过来的
sleep(1)
print('%s开始吃掉包子%s' % (name,baozi))
def producer():
c1 = consumer('vth')
c2 = consumer('ygm')
c1.__next__()
c2.__next__()
for i in range(20):
c1.send(i) # 产生包子给yield
c2.send(i)
producer()