本文仅作记录,原文地址:https://www.jianshu.com/p/87da832730f5
Python yield from 用法详解
yield from
是Python3.3新出现的句法
如果生成器函数需要产出另一个生成器生成的值,传统的解决方法是使用嵌套的for循环:
>>> def chain(*iterables): ... for it in iterables: ... for i in it: ... yield i >>> s = 'ABC' >>> t = tuple(range(3)) >>> list(chain(s, t)) ['A', 'B', 'C', 0, 1, 2]
chain 生成器函数把操作依次交给接收到的各个可迭代对象处理。
Python3.3之后引入了新语法: >>> def chain(*iterables): ... for i in iterables: ... yield from i ... >>> list(chain(s, t)) ['A', 'B', 'C', 0, 1, 2]
yield from
完全代替了内层的 for 循环。yield from x
表达式对 x 对象所做的第一件事是,调用 iter(x),从中获取迭代器。因yield from
代码读起来更顺畅,不过感觉更像是语法糖。上面这个例子看上去比较简单(传统意义上说因为我们只是for循环一次就完事,因为只嵌套了一层),我们再来看几个yield from
的例子。
例子1:我们有一个嵌套型的序列,想将它扁平化处理为一列单独的值。
from collections import Iterable def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): yield from flatten(x) else: yield x items = [1, 2, [3, 4, [5, 6], 7], 8] for x in flatten(items): print(x) # output: 1 2 3 4 5 6 7 8 ----------------------------------------------- items = ['Dave', 'Paula', ['Thomas', 'Lewis']] for x in flatten(items): print(x) # output: Dave Paula Thomas Lewis
collections.Iterable
是一个抽象基类,我们用isinstance(x, Iterable)
检查某个元素是否是可迭代的.如果是的话,那么就用yield from
将这个可迭代对象作为一种子例程进行递归。最终返回结果就是一个没有嵌套的单值序列了。ignore types
和检测语句isinstance(x, ignore types)
用来将字符yield from
的话,那么就需要另外一个for来嵌套,并不是一种优雅的操作例子2:利用一个Node类来表示树结构
class Node: def __init__(self, value): self._value = value self._children = [] def __repr__(self): return 'Node({!r})'.format(self._value) def add_child(self, node): self._children.append(node) def __iter__(self): return iter(self._children) def depth_first(self): yield self for c in self: yield from c.depth_first() if __name__ == '__main__': root = Node(0) child1 = Node(1) child2 = Node(2) root.add_child(child1) root.add_child(child2) child1.add_child(Node(3)) child1.add_child(Node(4)) child2.add_child(Node(5)) for ch in root.depth_first(): print(ch)
__iter__
代表一个Pyton的迭代协议,返回一个迭代器对象,就能迭代了depth_frist
返回一个生成器,仔细体会其中的yield
与 yield from
用法上面两个例子无论是树还是嵌套序列,都比较复杂,观察这里yield from
跟的是什么,跟的是函数,生成器函数,而且都是在函数内递归。虽然我也不是理解的很透彻 =,= 。但现在应该知道,这是yield from
一种常用的方法了(认真体会,手动滑稽)。
如果 yield from
结构唯一的作用是替代产出值的嵌套 for 循环,这个结构很有可能不会添加到 Python 语言中。yield from
结构的本质作用无法通过简单的可迭代对象说明,而要发散思维,使用嵌套的生成器。
yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。有了这个结构,协程可以通过以前不可能的方式委托职责。
这里有张详细图来说明三者关系:http://flupy.org/resources/yield-from.pdf
总结:
yield from
常用来代替内层for循环 与 打开双通道yield from
并不单独使用,而是伴随着asyncio
库使用,实现异步操作(一异步操作后面讲)async
和 await
,而await替代的就是yield from
(为了不与实现内层for循环的yield from
误解)