admin管理员组文章数量:1585968
目录
一、生成器函数描述
二、简单生成器
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.
五、异步生成器 (未填坑)
留坑。
版权声明:本文标题:Python——生成器(Generator) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/xitong/1727971037a1140169.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论