@asyncio.coroutine vs async def [英] @asyncio.coroutine vs async def

查看:21
本文介绍了@asyncio.coroutine vs async def的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用我见过的 asyncio

@asyncio.coroutine定义函数():...

异步定义函数():...

可互换使用.

两者在功能上有区别吗?

解决方案

是的,使用 async def 语法的原生协程和使用 asyncio.coroutine 的基于生成器的协程之间存在功能差异 装饰器.

根据 PEP 492,它引入了async def 语法:

<块引用>

  1. Native coroutine 对象没有实现 __iter__ 并且__next__ 方法.因此,它们不能被迭代或传递到 iter()list()tuple() 和其他内置函数.他们也不能在 for..in 循环中使用.

    尝试在原生协程上使用 __iter____next__对象将导致 TypeError .

  2. 普通生成器不能yield from native coroutines:这样做将导致 TypeError .

  3. 基于生成器的协程(对于 asyncio 代码必须用@asyncio.coroutine) 可以yield from 原生协程对象.

  4. inspect.isgenerator()inspect.isgeneratorfunction()原生协程返回False对象和原生协程函数.

上面的第 1 点意味着,虽然使用 @asyncio.coroutine 装饰器语法定义的协程函数可以像传统的生成器函数一样运行,但使用 async def 语法定义的协程函数不能.

这里有两个最小的、表面上等效的协程函数,用两种语法定义:

导入异步@asyncio.coroutine定义装饰(x):来自 x 的收益异步定义本机(x):等待 x

虽然这两个函数的字节码几乎相同:

<预><代码>>>>导入文件>>>dis.dis(装饰)5 0 LOAD_FAST 0 (x)3 GET_YIELD_FROM_ITER4 LOAD_CONST 0(无)7 YIELD_FROM8 POP_TOP9 LOAD_CONST 0(无)12 RETURN_VALUE>>>dis.dis(本地)8 0 LOAD_FAST 0 (x)3 GET_AWAITABLE4 LOAD_CONST 0(无)7 YIELD_FROM8 POP_TOP9 LOAD_CONST 0(无)12 RETURN_VALUE

...唯一的区别是 GET_YIELD_FROM_ITERGET_AWAITABLE,当尝试迭代它们返回的对象时,它们的行为完全不同:

<预><代码>>>>列表(装饰('foo'))['f', 'o', 'o']

<预><代码>>>>列表(本机('foo'))回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:协程"对象不可迭代

显然 'foo' 不是可等待的,所以尝试用它调用 native() 没有多大意义,但希望这一点很清楚它返回的 coroutine 对象是不可迭代的,无论其参数如何.

Brett Cannon 对 async/await 语法的更详细调查:在 Python 3.5 中 async/await 到底是如何工作的? 更深入地介绍了这种差异.

With the asyncio library I've seen,

@asyncio.coroutine
def function():
    ...

and

async def function():
    ...

used interchangeably.

Is there any functional difference between the two?

解决方案

Yes, there are functional differences between native coroutines using async def syntax and generator-based coroutines using the asyncio.coroutine decorator.

According to PEP 492, which introduces the async def syntax:

  1. Native coroutine objects do not implement __iter__ and __next__ methods. Therefore, they cannot be iterated over or passed to iter(), list(), tuple() and other built-ins. They also cannot be used in a for..in loop.

    An attempt to use __iter__ or __next__ on a native coroutine object will result in a TypeError .

  2. Plain generators cannot yield from native coroutines: doing so will result in a TypeError .

  3. generator-based coroutines (for asyncio code must be decorated with @asyncio.coroutine) can yield from native coroutine objects.

  4. inspect.isgenerator() and inspect.isgeneratorfunction() return False for native coroutine objects and native coroutine functions.

Point 1 above means that while coroutine functions defined using the @asyncio.coroutine decorator syntax can behave as traditional generator functions, those defined with the async def syntax cannot.

Here are two minimal, ostensibly equivalent coroutine functions defined with the two syntaxes:

import asyncio

@asyncio.coroutine
def decorated(x):
    yield from x 

async def native(x):
    await x 

Although the bytecode for these two functions is almost identical:

>>> import dis
>>> dis.dis(decorated)
  5           0 LOAD_FAST                0 (x)
              3 GET_YIELD_FROM_ITER
              4 LOAD_CONST               0 (None)
              7 YIELD_FROM
              8 POP_TOP
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE
>>> dis.dis(native)
  8           0 LOAD_FAST                0 (x)
              3 GET_AWAITABLE
              4 LOAD_CONST               0 (None)
              7 YIELD_FROM
              8 POP_TOP
              9 LOAD_CONST               0 (None)
             12 RETURN_VALUE

... the only difference being GET_YIELD_FROM_ITER vs GET_AWAITABLE, they behave completely differently when an attempt is made to iterate over the objects they return:

>>> list(decorated('foo'))
['f', 'o', 'o']

>>> list(native('foo'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'coroutine' object is not iterable

Obviously 'foo' is not an awaitable, so the attempt to call native() with it doesn't make much sense, but the point is hopefully clear that the coroutine object it returns is not iterable, regardless of its argument.

A more detailed investigation of the async/await syntax by Brett Cannon: How the heck does async/await work in Python 3.5? covers this difference in more depth.

这篇关于@asyncio.coroutine vs async def的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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