【问题标题】:why the coroutine do not print the initial value None?为什么协程不打印初始值None?
【发布时间】:2015-01-27 05:10:27
【问题描述】:

这是follow-up co-routine question to another question。这段代码sn-p也摘自david beazley的示例代码(inline1.py)

为什么Got: None 没有打印出来?

class Task:

    def __init__(self, gen):
        self._gen = gen 

    def step(self, value=None):
        # Run to the next yield
        try:
            fut = self._gen.send(value)
            # Future returned
            fut.add_done_callback(self._wakeup)
        except StopIteration as exc:
            pass

    def _wakeup(self, fut):
        # Handler of results
        result = fut.result()
        #print(result)
        self.step(result) # Feedback loop(run to next yield)

if __name__ == '__main__':

    from concurrent.futures import ThreadPoolExecutor
    import time

    pool = ThreadPoolExecutor(max_workers=8)

    def func(x, y): 
        time.sleep(1)
        return x + y 

    def do_func(x, y): 
        result = yield pool.submit(func, x, y)
        print('Got:', result)

    t = Task(do_func(2,3))
    t.step()

【问题讨论】:

  • 这与 SSCCE 几乎相反。
  • @CharlesDuffy 感谢 cmets。你介意分享你对哪里改进的建议吗?谢谢。
  • sscce.org 将是开始的地方 - 或 stackoverflow.com/help/mcve,本地等效项。

标签: python coroutine


【解决方案1】:

经过一番挖掘,我在这里发布我的答案。

了解 s.send(None) 与 s.next()

首先,我们需要了解s.send(None)s.next()

这里有一个例子来说明s.send(None)

做同样的事情

s.next() 将生成器推进到 yield 语句。

摘自大卫的幻灯片:

def line_splitter(delimiter=None):
    print("Ready to Split")
    result = None
    while True:
        print("not yielded")
        line = (yield result)
        print("yielded.. [line = %s]" % line)
        result = line.split(delimiter)


if __name__ == "__main__":
    s = line_splitter(",")
    s.send(None)  # or use s.next()
    s.send("A,B,C")

Result:
>>> s = line_splitter(",")
>>> s.send(None)
Ready to Split
not yielded
>>> s.send('A,B,C')
yielded.. [line = A,B,C]
not yielded
['A', 'B', 'C']  # <-- Pause at line = (yield result)
                 # yield the result and wait for the next input from send()  


>>> s.send('E,F') # Now it receives the next input, so run to next (yield)
Yielded.. [line = E,F]
Not Yielded
['E', 'F']
>>>

现在,我们可以访问 inline1.py 来浏览代码流程。

1) 当t.step()被调用时,会运行fut = self._gen.send(None)发送None前进

do_func() 中生成语句的生成器

fut = do_func(2,3).send(None)

def do_func(x, y): 
    result = yield pool.submit(func, x, y)
    print('Got:', result)

这里理解代码流的关键是generator.send(None)的功能。

send(None) 用于提前生成器启动。这意味着,代码将在

处暂停

result = yield pool.submit(func, 2,3) 但将pool.submit(func,2,3) 返回到send()

与此同时,pool.submit(func, 2, 3) 正在执行中。

然后,它在这里暂停等待下一个值被发送来填充结果

==> result = (yield)(简体)或result = yield pool.submit(func, 2,3)

但是,我们使用fut.add_done_callback(self._wakeup)来处理返回fut

pool.submit(func,2,3),其结果等于5

因此,当调用add_done_callback 时,_wakeup() 将检索结果

来自fut.result(),即5。然后再次将5 推入step()

2) 然后,就到了下一个fut = self._gen.send(5)

现在,值5 被发送到结果中。所以,result = 5

然后,它会在此处打印出Got: 5

对于fut,没有更多的数据返回到send(),因此正在引发 StopIteration

然后,程序结束。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-11
    • 2021-12-30
    • 2022-12-20
    • 2017-03-27
    • 2020-07-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多