yield 关键字初探
yield 关键字在 python 中有诸多应用,例如生成器,协程并发等场景
yield 解释
yiled 是属于 python 中的一个关键字,其用法和 return 类似,但是 return 是返回所有的变量值,yield 会保存一个生成器中的上下文,待下次 yield 时,调用 __next__
方法,返回生成器中的下一个值,直到结束。
生成器和迭代器都是可迭代对象,但是区别是 生成器在调用 __next__
方法时,会逐个返回,保留上下文,而迭代器会直接返回所有的数据。
生成器
如果在一个方法内,包含了yield
关键字,那么这个函数就是一个 「 生成器 」。
1 | def gen(n): |
此外,生成器除了和迭代器一样实现迭代数据之外,还包含了其他方法:
generator.__next__()
: 执行for
时调用此方法,每次执行到yield
就会停止,然后返回yield
后面的值,如果没有数据可迭代,
抛出StopIterator
异常,for
循环结束。1
2
3
4gen = count(2)
print gen.next() # 0
print gen.next() # 1
print gen.next() # StopIterationgenerator.send(value)
:外部传入一个值到生成器内部,改变yield
前面的值这里有个例子:
1
2
3
4
5
6
7
8
9
10
11def count(n):
x = 0
while x < n:
value = yield x
if value is not None:
print(f'Received value: {value}')
x += 1
gen = count(5)
print(gen.__next__()) # print 0
print(gen.send('Hello')) # Received value: Hello, then print 1在上述代码中,我们通过 send 方法传递一个值到生成器中,将 yield x 传递给一个变量 x, 并打印出来;
简单的说,
send()
就是next()
的功能,加上传值给yield
。如果你有兴趣看下Python的源码,你会发现,其实next()
的实现,就是send(None)
。generator.throw(type[,value[,traceback]])
: 外部向生成器抛出一个异常1
2
3
4
5
6
7
8
9
10
11def throw_gen():
try:
yield 'Normal'
except ValueError:
yield 'Error'
finally:
print('Finally')
gen = throw_gen()
print(gen.__next__()) # Normal
print(gen.__next__()) # Finally, then StopIteration和预期结果一样,在创建完一个生成器后,使用 next 方法进行迭代,依次输出:Normal,Finally
当我们想在自定义输入特定 error 时,就可以使用 throw 方法
1
2
3
4gen = throw_gen()
print(gen.next()) # Normal
print(gen.throw(ValueError)) # Error
print(gen.next()) # Finally, then StopIterationgenerator.cloase()
: 关闭生成器
顾名思义,close() 方法就是关闭生成器。生成器被关闭后,再次调用 next 方法时,不管能否遇到 yield 关键字,都会立即抛出 “StopIteration” 异常。
生产者-消费者模型
yield 实现
1 | import time |
上述例子中,consumer 消费者是一个生成器,每次执行到 yield 时挂起,等待生产者的输入,生产者依次调用 send 方法,传入外部值到 消费者中,这样,我们就实现了一个(伪)并发。