内容简介:Python async and await
PEP 492
在 Python 3.5 之前协程是通过生成器实现的(PEP 342),在 PEP 380 中引入 yield from
语法得到进一步增强。这种实现方式有一些缺点:
- 协程和常规(regular)生成器有着相同的语法,所以他们容易被混淆,尤其对于新人
-
函数是否是协程是由函数体中是否存在
yield或yield from所决定。重构时在函数体中添加或删除这些语句时会产生不明显的错误 -
对异步调用的支持仅限于在语法上允许使用
yield的表达式,一些有用的语法特性被限制,比如with和for语句
为了消除生成器和协程之间的模糊关系,PEP 492 试图引入 async/await
, 实现原生协程(native coroutine),将这两个概念分离。
It is proposed to make coroutines a proper standalone concept in Python, and introduce new supporting syntax. The ultimate goal is to help establish a common, easily approachable, mental model of asynchronous programming in Python and make it as close to synchronous programming as possible.
async def
和 await
下面的语法用来声明一个原生协程
async def read_data(db):
pass
-
async def的函数总是一个协程,即使其中不包含await -
async函数中包含yield和yield from会是个语法错误(SyntaxError) -
code对象添加了两个新的 flagCO_COROUTINE和CO_ITERABLE_COROUTINE - 调用一个生成器时返回生成器对象,类似地,协程返回协程对象
-
StopIteration异常不会传播到协程外部,取而代之的是RuntimeError - When a native coroutine is garbage collected, a RuntimeWarning is raised if it was never awaited on
await
可用来获取一个协程的执行结果
async def read_data(db):
data = await db.fetch('SELECT ...')
...
await
和 yield from
相似,挂起 read_data
的执行直到 db.fetch
完成 await
只接受 awaitable,它可以为以下某一种
- 原生协程对象
-
经过
tyes.coroutine()装饰的基于生成器的协程对象 -
实现了
__await__()的对象 -
使用 CPython 的 C API
tp_as_async.am_await函数修饰的对象
在 async def
函数外使用 await
会发生语法错误,就像在函数外使用 yield
一样
async with
使用 async with
可以创建异步上线文管理器,能够在 enter 和 exit 时挂起。需要实现新的协议 __aenter__()
和 __aexit__()
,两者都要返回 awaitable
class AsyncContextManager:
async def __aenter__(self):
await log('entering context')
async def __aexit__(self, exc_type, exc, tb):
await log('exiting context')
像常规的 with
语句一样,可以在 async with
中使用多个上下文管理器。 async with
只能在 async def
函数内部使用
异步迭代器和 async for
异步 iterable 能够在其 iter 的实现中调用异步代码;异步 iterator 能狗在其 next 方法中调用异步代码。异步迭代需要以下支持
-
一个对象必须实现
__aiter__()方法(或者使用 CPython 的 C APItp_as_async.am_aiter)返回一个异步 iterator 对象 -
一个异步 iterator 对象必须实现
__anext__()方法(或者使用 CPython 的 C APItp_as_async.am_anext)返回 awaitable -
为了停止迭代
__anext__()方法需要抛出StopAsyncIteration异常
class AsyncIterable:
def __aiter__(self):
return self
async def __anext__(self):
data = await self.fetch_data()
if data:
return data
else:
raise StopAsyncIteration
async def fetch_data(self):
...
async for
必须在 async def
函数内部使用,如果向 async for
传入一个不含有 __aiter__()
的常规 iterable 会抛出 TypeError
。 async for
向通常的 for
一样可以有 else
下面的 工具 类实现了常规的 iterable 向异步 iterable 的转换。虽然这不是一件非常有用的事情,但说明了二者之间的关系
class AsyncIteratorWrapper:
def __init__(self, obj):
self._it = iter(obj)
def __aiter__(self):
return self
async def __anext__(self):
try:
value = next(self._it)
except StopIteration:
raise StopAsyncIteration
return value
async for letter in AsyncIteratorWrapper("abc"):
print(letter)
另外 PEP 492 中还解答了下面这些问题
为什么 StopIteration 变为 RuntimeError, 并使用 StopAsyncIteration 为什么 await 必须要在 async def 内 为什么不重用已有的方法 为什么不使用 for 和 with
PEP 525
(Python 3.6 中实现)PEP 525 中描述了异步生成器(asynchronous generators),注意这里说的不是用生成器实现异步,而是异步的生成器。异步生成器是为了进一步扩展 Python 的异步能力。常规的生成器在 PEP 255 中引入,目的是为了提供一种和迭代器行为相似,更简洁优雅的的生成复杂数据的途径。然而目前并没有等价的概念用于异步迭代器协议(asynchronous iteration protocol) async for
。要想使用这种语法必须要定义一个类实现 __aiter__
和 __anext__
,这使得写异步数据生成器存在着不必要的麻烦。
另外,据 PEP 525 中所言
Performance is an additional point for this proposal: in our testing of the reference implementation, asynchronous generators are 2x faster than an equivalent implemented as an asynchronous iterator.
异步生成器的执行速度是对应异步迭代器的两倍
class Ticker:
"""Yield numbers from 0 to `to` every `delay` seconds."""
def __init__(self, delay, to):
self.delay = delay
self.i = 0
self.to = to
def __aiter__(self):
return self
async def __anext__(self):
i = self.i
if i >= self.to:
raise StopAsyncIteration
self.i += 1
if i:
await asyncio.sleep(self.delay)
return i
使用异步生成器的等价实现
async def ticker(delay, to):
"""Yield numbers from 0 to `to` every `delay` seconds."""
for i in range(to):
yield i
await asyncio.sleep(delay)
调用异步生成器函数会返回一个实现了异步迭代协议(PEP 492)的异步生成器对象。异步生成器中不能包含非空 return
语句
完整的例子
import asyncio
import time
async def ticker(delay, to):
for i in range(to):
yield i
await asyncio.sleep(delay)
async def run():
async for i in ticker(1, 10):
print(i)
import asyncio
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(run())
finally:
loop.close()
Reference
PEP 342 -- Coroutines via Enhanced Generators PEP 492 -- Coroutines with async and await syntax PEP 525 -- Asynchronous Generators
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
《生活大爆炸》之科学揭秘
乔治·毕姆 / 韩准、徐漪、江业华、叶夜 / 世界图书出版公司 / 2012-12 / 49.00元
《 之科学揭秘:GEEK探索频道》对流行美剧《生活大爆炸》进行“深度解密”,重点在解读剧中涉及的流行文化及科学元素。正如我们所知,《生活大爆炸》是一部“技术含量很高”的肥皂剧。不光是普通观众,科学家也爱《生活大爆炸》。《 之科学揭秘:GEEK探索频道》中,科学家详尽为你解释了电视剧中出现的科学道理和典故。包括谢尔顿的高深弦理论、霍华德的花生过敏是怎么回事、如果你和谢尔顿的妈妈有同样的信仰该如何看待......一起来看看 《《生活大爆炸》之科学揭秘》 这本书的介绍吧!
HTML 编码/解码
HTML 编码/解码
XML 在线格式化
在线 XML 格式化压缩工具