Python自身协同程序和send() [英] Python native coroutines and send()

查看:149
本文介绍了Python自身协同程序和send()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据发电机协同程序有一个发送()方法,它允许呼叫者和被叫方之间的双向通信。然后返回到调用方产生发电机协程。这是原来的发电机到协同程序的功能。

虽然新的原生异步/的await 协同程序提供异步I上级支持/ O,我不知道如何让相当于发()他们。异步功能使用收益被明确禁止,因此本地协同程序可以使用<$ C $返回只有一次C>收益语句。虽然等待前pressions带来新的价值成协程,这些值来自被调用,而不是调用,并且等待呼叫从一开始每次进行评估,不从那里离开。

有没有办法恢复从上次停止的地方返回的协同程序,并在新的价值可能会派?
我怎样才能效法大卫比兹利的的协同程序和并发使用本机协同程序的好奇课程中的技术?

一般code模式我想到的是一样的东西。

  DEF myCoroutine():
  ...
  而真正的:
    ...
    平=收益率(乒乓球)
    ...

和调用方

 ,而真:
  ...
  嗡嗡= myCoroutineGen.send(百事达)
  ...


修改

我接受凯文的答案,但我已经注意到,PEP


  

协同程序是基于发电机内部,因此它们共享的实现。同样到发电机的对象,协同程序有掷(),送()和close()方法。


...


  

掷()用于协同程序方法来推动价值和提高误差为未来状物体,送()。


因此​​很明显,本土协同程序确实有一个发送()?它是如何不收益前pression工作,收到协程内的价值?


解决方案

  

有没有办法恢复从上次停止的地方返回的协同程序,并可能在发送一个新的价值?


没有。

异步等待是的只是的为语法糖从收益率。当协程返回(与收益语句),仅此而已。框架已经一去不复返了。这是不可恢复。这是发电机已经到底总是工作。例如:

  DEF富():
    回报(收益率)

您可以做 F = foo的();下一个(F); f.send(5),你会回来5.但是,如果你尝试 f.send()再次,这是行不通的,因为你已经从框架返回。 ˚F不再是一个活的发生器。

现在,随着新的协同程序,到目前为止,我所知道的,它似乎产生和发送保留给事件循环和某些基本的predicates如 asyncio.sleep之间的通信() 。该协同程序产生 asyncio.Future 的对象到事件循环,以及事件循环发送这些相同的未来对象返回到协程一旦相关的操作都已完成(它们经由 call_soon() 和其他事件循环的方法)。

您可以等待他们产生未来的对象,但它不是一个通用的接口,如。发送()了。它是专门用于由事件循环执行使用。如果不实施一个事件循环,你可能不希望被玩弄这一点。如果你的的实施事件循环,你需要问自己,为什么在 ASYNCIO 没有足够的完美的实现你的目的,并解释专门的你正在尝试做之前,我们可以帮助你。

请注意,收益率不pcated德$ P $。如果你想要一个不依赖于一个事件循环在所有的协同程序,只使用该来代替。 异步等待是的专为异步编程设计的事件循环。如果这不是你在做什么,那么异步等待都开始与错误的工具。

一件事:


  

使用在异步功能产量被明确禁止,因此本地协同程序可以使用收益回报只有一次声明。


等待前pressions 的产量控制。 等待来自一些东西()完全类似于收益率()。他们只是改变了名称,这样它会更直观的人不熟悉的发电机。


对于那些你究竟是谁有意实现自己的事件循环,<一个href=\"https://bitbucket.org/NYKevin/nbtparse/src/e1aec5c6b1691d305a94839791b308835cdadaaf/nbtparse/_utils.py?at=optionally-async&fileviewer=file-view-default#_utils.py-85\"相对=nofollow>这里的一些例子code 呈现(很小)实现。此事件循环非常剥离下来,因为它设计同步,如果他们正常功能运行某些特别编写的协同程序。它不提供全方位的支持,你会期望从一个真正的 BaseEventLoop 的实施,是不安全的任意协同程序使用。

通常情况下,我将包括code在我的答案,而不是链接到它,但也有版权问题,这是不是答案本身是至关重要的。

Generator based coroutines have a send() method which allow bidirectional communication between the caller and the callee and resumes a yielded generator coroutine from the caller. This is the functionality that turns generators into coroutines.

While the new native async/await coroutines provide superior support for async I/O, I do not see how to get the equivalent of send() with them. The use of yield in async functions is explicitly forbidden, so native coroutines can return only once using a return statement. Although await expressions bring new values into a coroutine, those values come from callees, not the caller, and the awaited call is evaluated from the beginning each time, not from where it left off.

Is there a way to resume a returned coroutine from where it left off and potentially send in a new value? How can I emulate the techniques in David Beazley's Curious Course on Coroutines and Concurrency using native coroutines?

The general code pattern I have in mind is something like

def myCoroutine():
  ...
  while True:
    ...
    ping = yield(pong)
    ...

and in the caller

while True:
  ...
  buzz = myCoroutineGen.send(bizz)
  ...


Edit

I accepted Kevin's answer but I have noticed that the PEP says

Coroutines are based on generators internally, thus they share the implementation. Similarly to generator objects, coroutines have throw() , send() and close() methods.

...

throw() , send() methods for coroutines are used to push values and raise errors into Future-like objects.

So apparently native coroutines do have a send()? How does it work without yield expression to receive the values inside the coroutine?

解决方案

Is there a way to resume a returned coroutine from where it left off and potentially send in a new value?

No.

async and await are just syntactic sugar for yield from. When a coroutine returns (with the return statement), that's it. The frame is gone. It is not resumable. This is exactly how generators have always worked. For example:

def foo():
    return (yield)

You can do f = foo(); next(f); f.send(5), and you will get back 5. But if you try to f.send() again, it does not work, because you already returned from the frame. f is no longer a live generator.

Now, as for new coroutines, so far as I can tell, it seems yielding and sending is reserved for communication between the event loop and certain basic predicates such as asyncio.sleep(). The coroutines yield asyncio.Future objects up to the event loop, and the event loop sends those same future objects back into the coroutine once the associated operations have been completed (they are typically scheduled via call_soon() and the other event loop methods).

You can yield future objects by awaiting them, but it's not a general-purpose interface like .send() was. It is specifically intended for use by the event loop implementation. If you are not implementing an event loop, you probably do not want to be playing around with this. If you are implementing an event loop, you need to ask yourself why the perfectly good implementations in asyncio are not sufficient for your purposes and explain what specifically you are trying to do before we can help you.

Please note that yield from is not deprecated. If you want coroutines that are not tied to an event loop at all, just use that instead. async and await are specifically designed for asynchronous programming with event loops. If that is not what you are doing, then async and await are the wrong tool to begin with.

One more thing:

The use of yield in async functions is explicitly forbidden, so native coroutines can return only once using a return statement.

await expressions do yield control. await something() is entirely analogous to yield from something(). They just changed the name so it would be more intuitive to people not familiar with generators.


For those of you who actually are interested in implementing your own event loop, here's some example code showing a (very minimal) implementation. This event loop is extremely stripped down, because it is designed to run certain specially-written coroutines synchronously as if they were normal functions. It does not provide the full range of support you would expect from a real BaseEventLoop implementation, and is not safe for use with arbitrary coroutines.

Ordinarily, I would include the code in my answer, rather than linking to it, but there are copyright concerns and it is not critical to the answer itself.

这篇关于Python自身协同程序和send()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆