Python 生成器(generator)详细总结+示例
date
Feb 26, 2020
slug
5589752172931072
status
Published
tags
Python
summary
type
Post
简介
生成器(generator)是一种返回一个值的迭代器,每次从该迭代器取下一个值。
生成器有两种表示:
- 生成器表达式
- 生成器函数
生成器函数还包括一下内容:
> - 通用生成器
> - 协程生成器
> - 委托生成器
> - 子生成器
生成器表达式
生成器表达式是用圆括号来创建生成器,其语法与推导式相同,只是将
[]
换成了 ()
。 生成器表达式会产生一个新的生成器对象。In [1]: type([i for i in range(5)]) Out[1]: listIn [2]: type((i for i in range(5))) Out[2]: generator
这种生成器表达式被成为隐式生成器,他是禁止使用
yield
和 yield from
表达式。示例:
In [1]: a = (i for i in range(5)) In [2]: next(a) Out[2]: 0In [3]: next(a) Out[3]: 1In [4]: next(a) Out[4]: 2In [5]: next(a) Out[5]: 3In [6]: next(a) Out[6]: 4In [7]: next(a) ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-7-15841f3f11d4> in <module>----> 1 next(a) StopIteration:
In [1]: a = (i for i in range(5)) In [2]: for i in a: ...: print(i) ...: 01234
这好像和迭代器没什么区别!(如果你不清楚什么是迭代器请阅读 Python 迭代器的使用)
生成器函数
函数如果包含
yield
指令,该函数调用的返回值是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用 next
的时候才会真正执行里面的代码。yield
可以暂停一个函数并返回此时的中间结果。该函数将保存执行环境并在下一次恢复。In [1]: def fun(): ...: for i in range(5): ...: print("Start...") ...: yield i ...: In [2]: fun Out[2]: <function __main__.fun()>In [3]: fun() Out[3]: <generator object fun at 0x04F5D140>
示例:
In [1]: def fun(): ...: for i in range(5): ...: print("Start...") ...: yield i ...: In [2]: f = fun() In [3]: next(f) Start... Out[3]: 0In [4]: next(f) Start... Out[4]: 1In [5]: next(f) Start... Out[5]: 2In [6]: next(f) Start... Out[6]: 3In [7]: next(f) Start... Out[7]: 4In [8]: next(f) ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-8-aff1dd02a623> in <module>----> 1 next(f) StopIteration:
In [1]: def fun(): ...: for i in range(5): ...: print("Start...") ...: yield i ...: In [2]: for i in fun(): ...: print(i) ...: Start... 0Start... 1Start... 2Start... 3Start... 4
详解
如果一个函数包含
yield
关键字,那么这个函数的返回值是一个生成器,但是这个函数仅是一个函数而已。使用这个函数返回一个生成器对象并使用它才是真正的目的。此时,在生成器函数使用 return
对其并没有任何影响,这个函数返回的仍然是生成器对象。但是,如果没有 return
语句,则执行到函数完毕时将返回 StopIteration
异常。 如果在执行过程中遇到 return
语句,则直接抛出 StopIteration
异常,终止迭代。如果在 return
后返回一个值,那么这个值作为 StopIteration
异常的说明,不是函数的返回值。示例:
In [1]: def fun(): ...: for i in range(5): ...: print("Start...") ...: yield i ...: ...: return None ...: In [2]: fun Out[2]: <function __main__.fun()>In [3]: fun() Out[3]: <generator object fun at 0x04A05220>
当然,也可以通过
close()
手动关闭生成器函数,后面再调用生成器会直接返回 StopIteration
异常。示例:
In [1]: def fun(): ...: for i in range(5): ...: print("Start...") ...: yield i ...: In [2]: f = fun() In [3]: next(f) Start... Out[3]: 0In [4]: next(f) Start... Out[4]: 1In [5]: f.close() In [6]: next(f) ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-6-aff1dd02a623> in <module>----> 1 next(f) StopIteration:
向生成器发送数据
此外,生成器可以通过
send()
接收外部传入的值,语法为:
receive = yield value
receive
将收到 send
方法中的参数的值。注意:在启动生成器函数时只能
send(None)
或使用其他方法(next()
、__next__()
),如果试图输入其它的值都会得到错误提示信息。第一次执行之外的每次执行,首先是在上一次停止的
yield
之处把传入值赋给 yield
语句左侧的变量(如果有的话),当然,没有传入任何值的 receive
将赋予 None
,然后执行到下一个 yield
语句并返回其右侧表达式(如果有的话)的值。示例:
In [1]: def fun(): ...: for i in range(5): ...: print("Start...") ...: receive = yield i ...: print(receive) ...: In [2]: f = fun() In [3]: next(f) Start... Out[3]: 0In [4]: next(f) NoneStart... Out[4]: 1In [5]: next(f) NoneStart... Out[5]: 2In [6]: f.send("hello") hello Start... Out[6]: 3
为什么是这样
生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要迭代器的类一样写
__iter__()
和__next__()
方法了,只需要一个yield关键字。当一个生成器函数被调用的时候,它返回一个迭代器,称为生成器或者生成器对象。生成器一定是迭代器(反之不成立),因此任何生成器也是以一种惰性加载的模式生成值。什么意思呢,这个生成器对象来控制生成器函数的执行。当这个生成器的某一个方法被调用的时候,生成器函数开始执行。这时会一直执行到第一个 yield 语句,在此执行被挂起,并给生成器的调用者返回值。挂起后,所有局部状态都被保留下来,包括局部变量的当前绑定,指令指针,内部求值栈和任何异常处理的状态。通过调用生成器的某一个方法,生成器函数继续执行。生成器对象可以通过
next()
、__next__()
、send()
方法恢复函数执行。扩展
了解一下
如果生成器表达式包含
async for
子句或 await
表达式,则称为异步生成器表达式。 异步生成器表达式会返回一个新的异步生成器对象,此对象属于异步迭代器。如果在一个
async def
定义的函数体内使用 yield 表达式会让协程函数变成异步的生成器。(您如果您使用Python 版本小于 3.7 ,异步生成器表达式只能在 async def
携程中出现。 从 3.7 开始,任何函数都可以使用异步生成器表达式。)注:在 Python 3.8版本中,
yield
和 yield from
在隐式嵌套的作用域中已被禁用yield from 生成器
yield from
允许一个生成器将其部分操作委派给另一个生成器。示例:
In [1]: def f1(x): ...: receive = yield x ...: return receive ...: In [2]: def f2(): ...: for i in range(5): ...: rlt = yield from f1("你好") ...: print(rlt) ...: In [3]: f = f2() In [4]: next(f) Out[4]: '你好'In [5]: f.send("世界") 世界 Out[5]: '你好'
f1
、f2
是两个生成器函数,生成器函数 f2
通过 yield from
委托给生成器函数 f1
,当生成器函数恢复执行时通过 return
返回相应值给 f2
,这里 f2
被称为委托生成器,f1
被称为子生成器。通俗的说含有 yield from
关键字的函数是委托生成器, yield from
后的函数是子生成器。yield from
为调用者和子生成器之间提供了一个透明的双向通道,包括从子生成器获取数据以及向子生成器传送数据。虽然
yield from
主要设计用来向子生成器委派操作任务,但 yield from
可以向任意的迭代器委派操作。示例:
In [1]: def fun(): ...: yield from range(5) ...: In [2]: f = fun() In [3]: next(f) Out[3]: 0In [4]: next(f) Out[4]: 1In [5]: next(f) Out[5]: 2In [6]: next(f) Out[6]: 3In [7]: next(f) Out[7]: 4In [8]: next(f) ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-8-aff1dd02a623> in <module>----> 1 next(f) StopIteration:
yield from iterable 本质上等于 for item in iterable: yield item
委托生成器的作用:在调用方和子生成器之间行成双向通道。
双向通道的含义:调用方可以通过
send()
直接发送消息给子生成器,而子生成器 yield
的值,也是直接返回给调用方。只有子生成器结束(return
)了,yield from
左边的变量才会被赋值,委托生成器后面的代码才会执行。yield from
允许子生成器直接从调用者接收其发送的信息或者抛出调用时遇到的异常,并且返回给委托生产器一个值。注意
如果对子生成器的调用产生
StopIteration
异常,委托生成器恢复继续执行 yield from
后面的语句;示例:
In [1]: def f1(): ...: return ...: yield "hello" ...: In [2]: def f2(): ...: for i in range(5): ...: yield from f1() ...: print("f2") ...: In [3]: f = f2() In [4]: next(f) f2 f2 f2 f2 f2 ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-4-aff1dd02a623> in <module>----> 1 next(f) StopIteration:
若子生成器产生其他任何异常,则都传递给委托生成器。如果
GeneratorExit
异常被抛给委托生成器,或者委托生成产器的 close()
方法被调用,如果子生成器有close()
的话也将被调用。示例:
In [1]: def f1(x): ...: yield x ...: In [2]: def f2(): ...: for i in range(5): ...: yield from f1("f2") ...: In [3]: f11 =f1("f1") In [4]: f22 = f2() In [5]: next(f11) Out[5]: 'f1'In [6]: next(f22) Out[6]: 'f2'In [7]: f22.close() In [8]: next(f11) ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-8-28c9d6bd1302> in <module>----> 1 next(f11) StopIteration: In [9]: next(f22) ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-9-ba7a330f783c> in <module>----> 1 next(f22) StopIteration:
当子生成器结束并抛出异常时,
yield from
表达式的值是其 StopIteration
异常中的第一个参数。示例:
In [1]: def f1(): ...: yield ...: return "Hi" ...: In [2]: def f2(): ...: rlt = None ...: for i in range(5): ...: rlt = yield from f1() ...: print(rlt) ...: In [3]: f = f2() In [4]: next(f) In [5]: next(f) In [6]: next(f) In [7]: next(f) In [8]: next(f) In [9]: next(f) Hi ---------------------------------------------------------------------------StopIteration Traceback (most recent call last) <ipython-input-9-aff1dd02a623> in <module>----> 1 next(f) StopIteration: