快捷搜索:  汽车  科技

python生成器和代码(Python中的生成器)

python生成器和代码(Python中的生成器)有一个简单的将值传递给生成器的方法。yield 变成了一个表达式,返回一个可以赋给变量或执行操作的值:在以前的版本中,生成器只产生输出。一旦调用生成器的代码创建一个迭代器,就没有办法在函数恢复执行的时候向它传递新的信息。你可以设法实现这个功能,让生成器引用一个全局变量或者一个调用者可以修改的可变对象,但是这些方法都很繁杂。当你调用一个生成器函数,它并不会返回单独的值,而是返回一个支持生成器协议的生成器对象。当执行 yield 表达式时,生成器会输出 i 的值,就像 return 表达式一样。yield 和 return 最大的区别在于,到达 yield 的时候生成器的执行状态会挂起并保留局部变量。在下一次调用生成器 __next__() 方法的时候,函数会恢复执行。这里有一个 generate_ints() 生成器的示例:同样,你可以写出 for i in generate_ints(5)

生成器是一类用来简化编写迭代器工作的特殊函数。普通的函数计算并返回一个值,而生成器返回一个能返回数据流的迭代器。

当我在Python中调用普通函数时,函数会获得一个创建局部变量的私有命名空间。当函数到达 return 表达式时,局部变量会被销毁然后把返回给调用者。之后调用同样的函数时会创建一个新的私有命名空间和一组全新的局部变量。但是,如果在退出一个函数时不扔掉局部变量会如何呢?如果稍后你能够从退出函数的地方重新恢复又如何呢?这就是生成器所提供的;他们可以被看成可恢复的函数。

这里有简单的生成器函数示例:

def generate_ints(N): for i in range(N): yield i

任何包含了 yield 关键字的函数都是生成器函数

当你调用一个生成器函数,它并不会返回单独的值,而是返回一个支持生成器协议的生成器对象。当执行 yield 表达式时,生成器会输出 i 的值,就像 return 表达式一样。yield 和 return 最大的区别在于,到达 yield 的时候生成器的执行状态会挂起并保留局部变量。在下一次调用生成器 __next__() 方法的时候,函数会恢复执行。

这里有一个 generate_ints() 生成器的示例:

python生成器和代码(Python中的生成器)(1)

同样,你可以写出 for i in generate_ints(5),或者 a b c = generate_ints(3)。

python生成器和代码(Python中的生成器)(2)

向生成器传递值

在以前的版本中,生成器只产生输出。一旦调用生成器的代码创建一个迭代器,就没有办法在函数恢复执行的时候向它传递新的信息。你可以设法实现这个功能,让生成器引用一个全局变量或者一个调用者可以修改的可变对象,但是这些方法都很繁杂。

有一个简单的将值传递给生成器的方法。yield 变成了一个表达式,返回一个可以赋给变量或执行操作的值:

val = (yield i)

可以调用 send(value)() <generator.send> 方法向生成器发送值。这个方法会恢复执行生成器的代码,然后 yield 表达式返回特定的值。如果调用普通的 __next__`方法,``yield`() 会返回 None.

这里有一个简单的每次加1的计数器,并允许改变内部计数器的值。

python生成器和代码(Python中的生成器)(3)

因为 yield 很多时候会返回 None,所以你应该总是检查这个情况。不要在表达式中使用 yield 的值,除非你确定 send() 是唯一的用来恢复你的生成器函数的方法。

除了 send() 之外,生成器还有两个其他的方法:

  • throw(type value=None traceback=None) 用于在生成器内部抛出异常;这个异常会在生成器暂停执行的时候由 yield 表达式抛出。
  • generator.close() 会在生成器内部抛出 GeneratorExit 异常来结束迭代。当接收到这个异常时,生成器的代码会抛出 GeneratorExit 或者 StopIteration;捕捉这个异常作其他处理是非法的,并会出发 RuntimeError。close() 也会在 Python 垃圾回收器回收生成器的时候调用。
  • 如果你要在 GeneratorExit 发生的时候清理代码,我建议使用 try: ... finally: 组合来代替 GeneratorExit。

这些改变的累积效应是,让生成器从单向的信息生产者变成了既是生产者,又是消费者。

生成器也可以成为 协程 ,一种更广义的子过程形式。子过程可以从一个地方进入,然后从另一个地方退出(从函数的顶端进入,从 return 语句退出),而协程可以进入,退出,然后在很多不同的地方恢复(yield 语句)。

猜您喜欢: