yield 关键字初探

yield 关键字初探

yield 关键字在 python 中有诸多应用,例如生成器,协程并发等场景

yield 解释

yiled 是属于 python 中的一个关键字,其用法和 return 类似,但是 return 是返回所有的变量值,yield 会保存一个生成器中的上下文,待下次 yield 时,调用 __next__ 方法,返回生成器中的下一个值,直到结束。

生成器和迭代器都是可迭代对象,但是区别是 生成器在调用 __next__ 方法时,会逐个返回,保留上下文,而迭代器会直接返回所有的数据。

生成器

如果在一个方法内,包含了yield关键字,那么这个函数就是一个 「 生成器 」。

1
2
3
def gen(n):
for i in range(n):
yield i

此外,生成器除了和迭代器一样实现迭代数据之外,还包含了其他方法:

  • generator.__next__(): 执行 for 时调用此方法,每次执行到yield就会停止,然后返回yield后面的值,如果没有数据可迭代,
    抛出 StopIterator异常,for循环结束。

    1
    2
    3
    4
    gen = count(2)
    print gen.next() # 0
    print gen.next() # 1
    print gen.next() # StopIteration
  • generator.send(value)外部传入一个值到生成器内部,改变 yield 前面的值

    这里有个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def 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
    11
    def 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
    4
    gen = throw_gen()
    print(gen.next()) # Normal
    print(gen.throw(ValueError)) # Error
    print(gen.next()) # Finally, then StopIteration
  • generator.cloase(): 关闭生成器

顾名思义,close() 方法就是关闭生成器。生成器被关闭后,再次调用 next 方法时,不管能否遇到 yield 关键字,都会立即抛出 “StopIteration” 异常。

生产者-消费者模型

yield 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import time


def producer(c):
next(c)
for i in range(10):
time.sleep(1)
c.send(i)
print(f"producer: {i}")

c.close()


def consumer():
i = None
while True:
j = yield i
print(f"consumer: {j}")


if __name__ == '__main__':
c = consumer()
producer(c)

上述例子中,consumer 消费者是一个生成器,每次执行到 yield 时挂起,等待生产者的输入,生产者依次调用 send 方法,传入外部值到 消费者中,这样,我们就实现了一个(伪)并发。

-------------THANKS FOR READING-------------