Python——生成器(Generator)

目录

一、生成器函数描述

二、简单生成器

generator元素的打印(g.next)

三、带yield语句的生成器

yield用例

yield from 委托给子生成器的语法

 四、generator使用

generator.__next__()

generator.send(value)

generator.throw(type[, value[, traceback]])

generator.close()

五、异步生成器 (未填坑)


参考资料:python3.7.8文档


生成器:一边循环一边计算的机制。

一、生成器函数描述

在一个函数体内使用 yield 表达式会使这个函数变成一个生成器。

当一个生成器函数被调用的时候,它返回一个迭代器,称为生成器。

然后这个生成器来控制生成器函数的执行。当这个生成器的某一个方法被调用的时候,生成器函数开始执行。这时会一直执行到第一个 yield 表达式,在此执行再次被挂起,给生成器的调用者返回 expression_list 的值。

挂起后,我们说所有局部状态都被保留下来,包括局部变量的当前绑定,指令指针,内部求值栈和任何异常处理的状态。

通过调用生成器的某一个方法,生成器函数继续执行。此时函数的运行就和 yield 表达式只是一个外部函数调用的情况完全一致。恢复后 yield 表达式的值取决于调用的哪个方法来恢复执行。 如果用的是 __next__() (通常通过语言内置的 for 或是 next() 来调用) 那么结果就是 None. 否则,如果用 send(), 那么结果就是传递给send方法的值。

二、简单生成器

把一个列表生成式的[ ]改成( ),就创建了一个generator:

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x104feab40>

创建L和g的区别在于最外层的[]和(),L是一个list,而g是一个generator。

generator元素的打印(g.next)

>>> g.__next__()
0
>>> g.__next__()
1
>>> g.__next__()
4
>>> g.__next__()
9

加入for循环:

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
...
0
1
4
9
16
25
36
49
64
81

三、带yield语句的生成器

yield用例

用斐波那契数列作为例子:

def fib1(max):
    n, a, b = 0, 0, 1
    while n < max:
        print (b)
        a, b = b, a + b
        n = n + 1

从第一个元素开始,推算出后续任意元素,这样的逻辑与生成器非常类似。

使用带yield语句的生成器,代码变成:

def fib2(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1

 上面两个区别在于:

fib1是顺序执行的,遇到return语句或最后一行函数语句就返回。

generator函数(fib2)在每次调用__next__()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。(python2.x中为next())

用例:

>>> def odd():
...     print('step1')
...     yield 1
...     print('step2')
...     yield 3
...     print('step3')
...     yield 5
...
>>> o = odd()
>>> o.__next__()
step1
1
>>> o.__next__()
step2
3
>>> o.__next__()
step3
5
>>> o.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

如上面的例子所示:

generator在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,执行完毕,所以再次调用报错。 

对于上面的fib2,如果不设置终止条件,就会无限执行,所以设置了max,此外,通常不使用__next__()调用,而是使用for循环:

>>> def fib2(max):
...     n,a,b = 0,0,1
...     while n < max:
...             yield b
...             a,b = b,a+b
...             n = n+1
...
>>> for n in fib2(6):
...     print(n)
...
1
1
2
3
5
8

yield from 委托给子生成器的语法

yield from表达式,允许生成器将其部分操作委托给另一个生成器。这允许将包含yield的一段代码分离出来并放置到另一个生成器中。此外,允许子生成器返回一个值,并且该值可用于委托生成器。
虽然主要设计用于委托给子生成器,但expression的yield实际上允许委托给任意的子迭代器。
对于简单的迭代器,iterable的yield本质上只是iterable中For item的缩写形式:

>>> def g(x):
...     yield from range(x, 0, -1)
...     yield from range(x)
...
>>> list(g(5))
[5, 4, 3, 2, 1, 0, 1, 2, 3, 4]

 然而,与普通循环不同的是,yield from允许子生成器从调用范围直接接收发送和抛出的值,并向外部生成器返回一个最终值:

>>> def accumulate():
...     tally = 0
...     while 1:
...         next = yield
...         if next is None:
...             return tally
...         tally += next
...
>>> def gather_tallies(tallies):
...     while 1:
...         tally = yield from accumulate()
...         tallies.append(tally)
...
>>> tallies = []
>>> acc = gather_tallies(tallies)
>>> next(acc)  # Ensure the accumulator is ready to accept values
>>> for i in range(4):
...     acc.send(i)
...
>>> acc.send(None)  # Finish the first tally
>>> for i in range(5):
...     acc.send(i)
...
>>> acc.send(None)  # Finish the second tally
>>> tallies
[6, 10]

 四、generator使用

generator.__next__()

开始一个生成器函数的执行或是从上次执行的 yield 表达式位置恢复执行。 当一个生成器函数通过 __next__() 方法恢复执行时,当前的 yield 表达式总是取值为 None。 随后会继续执行到下一个 yield 表达式,其 expression_list 的值会返回给 __next__() 的调用者。 如果生成器没有产生下一个值就退出,则将引发 StopIteration 异常。

此方法通常是隐式地调用,例如通过 for 循环或是内置的 next() 函数。

generator.send(value)

恢复执行并向生成器函数“发送”一个值。 value 参数将成为当前 yield 表达式的结果。 send() 方法会返回生成器所产生的下一个值,或者如果生成器没有产生下一个值就退出则会引发 StopIteration。 当调用 send() 来启动生成器时,它必须以 None 作为调用参数,因为这时没有可以接收值的 yield 表达式。

generator.throw(type[, value[, traceback]])

在生成器暂停的位置引发 type 类型的异常,并返回该生成器函数所产生的下一个值。 如果生成器没有产生下一个值就退出,则将引发 StopIteration 异常。 如果生成器函数没有捕获传入的异常,或引发了另一个异常,则该异常会被传播给调用者。

generator.close()

在生成器函数暂停的位置引发 GeneratorExit。 如果之后生成器函数正常退出、关闭或引发 GeneratorExit (由于未捕获该异常) 则关闭并返回其调用者。 如果生成器产生了一个值,关闭会引发 RuntimeError。 如果生成器引发任何其他异常,它会被传播给调用者。 如果生成器已经由于异常或正常退出则 close() 不会做任何事。

>>> def echo(value=None):
...     print("Execution starts when 'next()' is called for the first time.")
...     try:
...         while True:
...             try:
...                 value = (yield value)
...             except Exception as e:
...                 value = e
...     finally:
...         print("Don't forget to clean up when 'close()' is called.")
...
>>> generator = echo(1)
>>> print(next(generator))
Execution starts when 'next()' is called for the first time.
1
>>> print(next(generator))
None
>>> print(generator.send(2))
2
>>> generator.throw(TypeError, "spam")
TypeError('spam',)
>>> generator.close()
Don't forget to clean up when 'close()' is called.

五、异步生成器 (未填坑)

留坑。

  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值