Python 生成器(generator)详细总结+示例

14 篇文章 1 订阅

简介

生成器(generator)是一种返回一个值的迭代器,每次从该迭代器取下一个值。

生成器有两种表示:

  • 生成器表达式
  • 生成器函数

生成器函数还包括一下内容:

  • 通用生成器
  • 协程生成器
  • 委托生成器
  • 子生成器

生成器表达式

生成器表达式是用圆括号来创建生成器,其语法与推导式相同,只是将 [] 换成了 () 。 生成器表达式会产生一个新的生成器对象。

In [1]: type([i for i in range(5)])
Out[1]: list

In [2]: type((i for i in range(5)))
Out[2]: generator

这种生成器表达式被成为隐式生成器,他是禁止使用 yieldyield from 表达式。

示例:

In [1]: a = (i for i in range(5))

In [2]: next(a)
Out[2]: 0

In [3]: next(a)
Out[3]: 1

In [4]: next(a)
Out[4]: 2

In [5]: next(a)
Out[5]: 3

In [6]: next(a)
Out[6]: 4

In [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)
   ...:
0
1
2
3
4

这好像和迭代器没什么区别!(如果你不清楚什么是迭代器请阅读 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]: 0

In [4]: next(f)
Start...
Out[4]: 1

In [5]: next(f)
Start...
Out[5]: 2

In [6]: next(f)
Start...
Out[6]: 3

In [7]: next(f)
Start...
Out[7]: 4

In [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...
0
Start...
1
Start...
2
Start...
3
Start...
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]: 0

In [4]: next(f)
Start...
Out[4]: 1

In [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]: 0

In [4]: next(f)
None
Start...
Out[4]: 1

In [5]: next(f)
None
Start...
Out[5]: 2

In [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版本中, yieldyield 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]: '你好'

f1f2 是两个生成器函数,生成器函数 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]: 0

In [4]: next(f)
Out[4]: 1

In [5]: next(f)
Out[5]: 2

In [6]: next(f)
Out[6]: 3

In [7]: next(f)
Out[7]: 4

In [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:
  • 21
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值