Python: generator, yield, yield from 详解

1.Generator Expressionshtml

生成器表达式是用小括号表示的简单生成器标记法:python

generator_expression ::= "(" expression comp_for ")"express

生成器表达式产生一个生成器对象,它的语法和for相似,出了它是被“()”包含,而不是[]或{};app

生成器表达式中变量的计算被延迟到__next__()函数的调用,然而最左边for循环子句被当即计算,这样,若是他有错误的话能够被当即看到。后面的for循环子句不能被当即计算,由于他们可能依赖于前面的for循环,例如(x*y for in range(10) for in bar(x)异步

python3.6之后,若是生成器出如今async def function中,那么async for子句和await表达式能够被理解为是异步的。若是生成器表达式包含async for子句或者await表达式,就叫作异步生成器表达式。异步生成器表达式产生一个新的异步生成器对象,它是一个异步迭代器。async

 

2. Yield Expressions函数

yield_atom       ::=  "("  ")"
yield_expression ::=  "yield" [ | "from" ]

Yield表达式用于定义一个生成器函数或异步生成器函数,所以只能被用于一个函数定义体内。在一个函数定义体中使用yield表达式使其成为生成器,
在一个async def函数体内使用yield表达式使协程函数成为一个异步生成器。例如:
def gen(): //定义一个生成器函数
  yield 123
async def agen(): //定义一个异步生成器函数
  yield 123

当一个生成器函数被调用,它返回一个迭代器,也叫生成器。这个生成器控制生成器函数的执行。当生成器的一个函数被调用的时候,生成器函数开始执行。
执行到第一个yield表达式时,挂起,返回expression_list的值给调用者。对于挂起,咱们指的是全部局部状态被保留,包含当前局部变量的绑定,
指令指针,内部调用栈,任何异常处理状态。当经过调用生成器的一个函数恢复执行时,函数的执行就好像是从外部再次调用yield表达式同样。恢复执行后,
yield表达式的值取决调用的方法。若是是__next__()被调用(通常经过for循环或者内置的next()函数),那么值是None。若是是send()被调用,
值是传给send的参数的值。

全部的这些使得生成器函数很是像协程;它产生屡次值,有多个入口点而且执行能够被挂起。惟一的不一样是,生成器函数yield后不能控制程序从哪里继续执行,
控制权老是传回给生成器的调用者。

yield表达式能够在try块的任何地方,若是生成器在被结束(到达零引用或者由于垃圾回收机制)以前没有被恢复执行,生成器的close函数被调用,所以finally
子句被执行。
当yield from <expr> 被使用,它把附加的表达式当成一个子迭代器,全部子迭代器产生的值被直接传回给当前生成器函数的调用者。当前生成器调用send的参数值
和调用throw的异常参数 都将被传给底层迭代器(子迭代器),若是他有对应的方法的话。不然,send致使AttributeError或者TypeError,而throw当即
raise传给他的异常。
当底层迭代器执行完成,StopIteration对象的value值变成这个yield from表达式的值。这个值能够被显式的设置当产生StopIteration时,或者自动设置,若是
子迭代器是一个生成器(子生成器返回一个值)yield_expressionexpression_listexpression
3. 生成器-迭代器 方法
这部分介绍生成器迭代器的方法,他们能够被用来控制生成器函数的执行。当生成器正在执行时调用这些函数将致使ValueError。
(1)generator.__next__()
开始生成器函数的执行或者从最后被执行的yield表达式中恢复执行,若是是恢复执行,yield表达式的值是None,继续执行到下一个yield表达式,
挂起,返回expression_list的值给__next__()的调用者。若是生成器没有再yield一个值,则产生一个StopIteration异常
这个方法通常被隐式调用,例如for循环,next()
(2)generator.send(value)
恢复执行而且发送一个值到生成器函数。这个value参数就是当前yield表达式的结果。send方法返回下一个生成器yield的值
若是没有yield,返回StopIteration。当用send来启动生成器,他的参数必须是None,由于没有yield表达式接收值。
(3)generator.throw(type[,value[,traceback]])
在生成器挂起的地方产生一个type类型的异常,而且返回下一个生成器函数yield的值,若是没有yield,返回StopIteration。
若是生成器函数没有捕获这个传进去的异常 ,或者产生了另外一个不一样的异常,那么将这个异常传递给调用者。
(4)generator.close()
在生成器挂起的地方产生一个GeneratorExit(),若是生成器以后优雅的退出,已经关闭,或者产生了一个GeneartorExit(不捕获该异常),close将返回到它的
调用者。若是生成器yield一个值,那么产生一个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.

PEP380 加入了yield from表达式,容许一个生成器委派部分操做给另外一个生成器。这能够剔除一部分包含yield的代码放到另外一个生成器。另外,
子生成器能够返回一个值,这个值对于委托生成器也是可用的。
虽然主要涉及用来委派一个子生成器,可是yield from 表达式事实上能够委派任何的子迭代器。
低于简单的迭代器, yield from iterable 本质上就是一个简短的形式:for item in iterable: yield 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 容许子生成器直接从调用区域接收send和throw的值,而且返回一个最后的值给外层生成器。示例以下:
>>> 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]
相关文章
相关标签/搜索