一个函数使用了yield就变成了一个生成器(generator),跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器
def test1():
aa = 'hi'
while True:
yield aa
print('i am aa')
def test2():
bb = 'hello'
while True:
yield bb
print('i am bb')
if __name__ == '__main__':
aa = test1()
print(next(aa)) # 返回aa并中断test1函数
print(next(aa)) # 从test1函数中断处执行,执行print,然后返回aa并中断test1函数
bb = test2()
print(bb.__next__()) # 也可以用__next__方法
运行结果:
hi
i am aa
hi
hello
原理:在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。并在下一次执行 next()方法时从当前位置继续运行 如果遇到yield 语句,会挂起函数的运行状态,并将yield 右边的表达式的值返回给next()的调用者, 挂起的时候会保存所有本地状态,包括局部变量,指令指针和内部堆栈信息,这样当下次再次调用next()时, 看起来yield 部分就像是调用了一个外部调用一样,可以接着往下执行
通过原理我们了解,生产器是调用一次执行一次,应用在大量计算的函数中,对内存是较友好的,比如经典的斐波那契数列
def fib(n):
a = b = 1
for i in range(n):
yield a
a, b = b, a + b
生产器还提供了send()方法来进行生成器间的互动,PS第一次调用时需要注意下
before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield expression.
def test1():
aa = 'hi'
while True:
cc = yield aa
yield cc
if __name__ == '__main__':
t = test1()
t.send(None) #启动
print(next(test1())) #遇到第一个yield返回,aa值'hi'
print(t.send('hello')) #cc被赋值'hello',yield cc返回'hello'
运行结果:
hi
hello
有了互动方法,来实现下生产者消费者
def consumer():
c = '消费者'
while True:
r = yield c
if not r:
return
print('开始消费{}'.format(r))
c = f'消费{r}完成'
def produce(c):
print(c.send(None))
for i in range(1,6):
print('开始生产{}'.format(i))
r = c.send(i)
print('消费者,{}'.format(r))
c.close()
if __name__ == '__main__':
c = consumer()
produce(c)
运行结果:
消费者
开始生产1
开始消费1
消费者,消费1完成
开始生产2
开始消费2
消费者,消费2完成
开始生产3
开始消费3
消费者,消费3完成
开始生产4
开始消费4
消费者,消费4完成
开始生产5
开始消费5
消费者,消费5完成