赛派号

老式吹风机和新式的有什么区别 Python中旧式和新式协程的调用/返回协议有什么区别?

本文介绍了Python中旧式和新式协程的调用/返回协议有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在从老式的协程过渡(其中,"yield"返回由"send"提供的值,但是它们实际上是生成器)到带有"async def"和"await"的新型协程.有几件事确实让我感到困惑.

I'm transitioning from old-style coroutines (where 'yield' returns a value supplied by 'send', butwhich are otherwise essentially generators) to new-style coroutines with 'async def' and 'await'.There are a couple of things that really puzzle me.

请考虑以下旧式协程,该协程可计算提供给通过发送",在每个点返回均值. (此示例摘自 Fluent的第16章Python (卢西亚诺·拉马略(Luciano Ramalho)).

Consider the following old-style coroutine that computes the running erage of numbers supplied toit by 'send', at each point returning the mean-so-far. (This example is from Chapter 16 of FluentPython by Luciano Ramalho.)

def erager(): total = 0.0 count = 0 erage = None while True: term = yield erage total += term count += 1 erage = total/count

如果我现在创建和初始化协程对象,则可以向其发送编号,它将返回运行中的对象平均:

If I now create and prime a coroutine object, I can send it numbers and it will return the runningerage:

>>> coro_g = erager() >>> next(coro_g) >>> coro_g.send(10) 10.0 >>> coro_g.send(30) 20.0 >>> coro_g.send(5) 15.0

...等等.问题是,如何用async/await编写这样的协程?那里有三点让我感到困惑.我是否正确理解它们?

...and so forth. The question is, how would such a coroutine be written with async/await? Thereare three points that confuse me. Do I understand them correctly?

1)在旧的样式中,任何人都可以将数字发送到平均器的同一实例.我可以通过围绕上述值coro_g并每次调用.send(N)时,无论从何处,N都添加到同一运行中全部的.但是,使用异步/等待,就无法发送值".每次您等待"协程等待具有其上下文和变量值的新实例.

1) In the old style, anybody can send numbers to the same instance of the erager. I can passaround the value coro_g above and every time .send(N) is called, no matter from where, N is added to the same runningtotal. With async/await, however, there's no way to "send in a value". Each time you 'await' acoroutine you await a new instance with its own context, its own variable values.

2)看来,异步def"协程将值交还给正在等待的事物的唯一方法这是为了返回"并因此失去背景.您不能从异步"内部调用"yield"def coroutine(或者,如果您这样做,则创建了一个异步生成器,不能与await一起使用).因此,异步def"协程无法计算值并手动处理像平均器一样,在保持上下文的同时将其删除.

2) It seems that the only way for an 'async def' coroutine to hand a value back to the thing awaitingit is to 'return' and hence lose context. You can't call 'yield' from inside an 'asyncdef' coroutine (or rather if you do you've created an async generator whichcan't be used with await). So an 'async def' coroutine can't compute a value and handit out while maintaining context, as erager does.

3)与(1)几乎相同:协程调用'await'时,它将等待一个特定的,可等待的,即等待等待的论点.这与旧式协程非常不同,后者会放弃控制并坐在周围等待任何人向他们发送东西.

3) Almost the same as (1): When a coroutine calls 'await' it waits for a single, specific awaitable,namely the argument to await. This is very unlike old-style coroutines, which give up control andsit around waiting for anyone to send something to them.

我意识到新的协程与旧的协程是不同的编码范例:带有事件循环,并且您使用队列之类的数据结构来使协程发出一个没有返回和丢失上下文.不幸的是,新旧共享相同的名称名称-协程--鉴于它们的调用/返回协议是如此不同.

I realize that the new coroutines are a distinct coding paradigm from the old ones: They're usedwith event loops, and you use data structures like queues to he the coroutine emit a value withoutreturning and losing context. It's kind of unfortunate and somewhat confusing that new and old share the samename---coroutine---given that their call/return protocols are so different.

推荐答案

直接关联这两个模型是可能的,也可能是有启发性的.实际上,现代协程与旧协程一样,是根据(通用)迭代器协议实现的.区别在于,迭代器的返回值会通过任意数量的协程调用程序自动向上传播(通过隐式yield from),而实际的返回值将打包为StopIteration异常.

It is possible, and perhaps instructive, to relate the two models pretty directly. Modern coroutines are actually implemented, like the old, in terms of the (generalized) iterator protocol. The difference is that return values from the iterator are automatically propagated upwards through any number of coroutine callers (via an implicit yield from), whereas actual return values are packaged into StopIteration exceptions.

此编排的目的是向驾驶员(假定的事件循环")告知可以恢复协程的条件.该驱动程序可以从一个不相关的堆栈帧中恢复协程,并且可以再次通过等待的对象将数据发送回执行中,因为它是驱动程序已知的唯一通道,同样,send通过yield from透明地进行通信

The purpose of this choreography is to inform the driver (the presumed "event loop") of the conditions under which a coroutine may be resumed. That driver may resume the coroutine from an unrelated stack frame and may send data back into the execution—via the awaited object, since it is the only channel known to the driver—again, much as send communicates transparently through a yield from.

此类双向通信的示例:

class Send: def __call__(self,x): self.value=x def __await__(self): yield self # same object for awaiter and driver raise StopIteration(self.value) async def add(x): return await Send()+x def plus(a,b): # a driver c=add(b) # Equivalent to next(c.__await__()) c.send(None)(a) try: c.send(None) except StopIteration as si: return si.value raise RuntimeError("Didn't resume/finish")

真正的驾驶员当然会在将send的结果识别为Send之后才决定调用该结果.

A real driver would of course decide to call the result of send only after recognizing it as a Send.

实际上,您不想自己驾驶现代协程;它们针对完全相反的方法进行了语法优化.但是,使用队列来处理通信的一个方向(如您已经提到的)将很简单:

Practically speaking, you don’t want to drive modern coroutines yourself; they are syntax optimized for exactly the opposite approach. It would, however, be straightforward to use a queue to handle one direction of the communication (as you already noted):

async def g(q): n=s=0 while True: x=await q.get() if x is None: break n+=1; s+=x yield s/n async def test(): q=asyncio.Queue() i=iter([10,30,5]) await q.put(next(i)) async for a in g(q): print(a) await q.put(next(i,None))

以这种方式提供值有些痛苦,但是如果它们来自另一个Queue左右,则很容易.

Providing values that way is a bit painful, but it’s easy if they’re coming from another Queue or so.

这篇关于Python中旧式和新式协程的调用/返回协议有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lsinopec@gmail.com举报,一经查实,本站将立刻删除。

上一篇 没有了

下一篇没有了