DarkMatter in Cyberspace
  • Home
  • Categories
  • Tags
  • Archives

Python的Coroutine和yield from


yield from

yield x中,x是一个普通对象,例如3或者[1, 2, 3], yield from x中,x是一个iterator,例如下面的代码 来自what's the difference between yield from and yield in python 3.3.2+:

In [1]: def iterable1():
   ...:     yield 1
   ...:     yield 2
   ...: 
   ...: def iterable2():
   ...:     yield from iterable1()
   ...:     yield 3
   ...: 

In [2]: iter = iterable2()

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

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

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

In [6]: next(iter)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-6-5c05586d40e8> in <module>()
----> 1 next(iter)

StopIteration:

用PEP 380中的术语,iterable2()是generator,iterable1()是subgenerator, iterable2通过使用yield from实现对iterable1的“代理”, 这样上层iterator的代码就可以分散在几个subitorator中, 然后在上层iterator中,用yield from将这些subitorators组合起来。

再比如下面这个函数,更是直接判断x是不是iterator,如果是用yield from, 否则用yield:

def flatten(sequence):
    """flatten a multi level list or something
    >>> list(flatten([1, [2], 3]))
    [1, 2, 3]
    >>> list(flatten([1, [2], [3, [4]]]))
    [1, 2, 3, 4]
    """
    for element in sequence:
        if hasattr(element, '__iter__'):
            yield from flatten(element)
        else:
            yield element

yield from的另一个常用方法是后面跟一个range对象,例如:

>>> 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 x等价于for item in x: yield item(来自PEP 380)。

coroutine

使用coroutine可以在一个线程中同时执行多项任务,在多个函数中切换任务和发送消息。 例如下面的代码来自python generator “send” function purpose?:

def coroutine():
    for i in range(1, 5):
        print("From generator {}".format((yield i)))

c = coroutine()
c.send(None)
try:
    while True:
        print("From user {}".format(c.send(1)))
except StopIteration: pass

输出:

From generator 1
From user 2
From generator 1
From user 3
From generator 1
From user 4
From generator 1

接收方通过yield i拿到发送方发来的值(即send()的参数1), 发送方则通过c.send(1)拿到了接收方的当前状态(yield i中的i值), 这样就实现了二者间的双向数据传递, 且双方可以在任何时候停下来让对方工作,等待对方处理到一定阶段时把执行权还给自己, 这样就在一个线程里实现了多个任务同时进行的效果。

另一个使用协程实现“生产-消费者模型”的例子可以参考理解python coroutine.



Published

Jan 21, 2017

Last Updated

Jan 21, 2017

Category

Tech

Tags

  • python 136
  • yield 2

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor