python async with和async for的使用


Posted in Python onJune 20, 2019

网上async with和async for的中文资料比较少,我把PEP 492中的官方陈述翻译一下。

异步上下文管理器”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')

新语法

异步上下文管理器使用一种新的语法:

async with EXPR as VAR:
  BLOCK

这段代码在语义上等同于:

mgr = (EXPR)
aexit = type(mgr).__aexit__
aenter = type(mgr).__aenter__(mgr)
exc = True

VAR = await aenter
try:
  BLOCK
except:
  if not await aexit(mgr, *sys.exc_info()):
    raise
else:
  await aexit(mgr, None, None, None)

和常规的with表达式一样,可以在一个async with表达式中指定多个上下文管理器。

如果向async with表达式传入的上下文管理器中没有__aenter__ 和__aexit__方法,这将引起一个错误 。如果在async def函数外面使用async with,将引起一个SyntaxError(语法错误)。

例子

使用async with能够很容易地实现一个数据库事务管理器。

async def commit(session, data):
  ...

  async with session.transaction():
    ...
    await session.update(data)
    ...

需要使用锁的代码也很简单:

async with lock:
  ...

而不是:

with (yield from lock):
  ...

异步迭代器 “async for”

一个异步可迭代对象(asynchronous iterable)能够在迭代过程中调用异步代码,而异步迭代器就是能够在next方法中调用异步代码。为了支持异步迭代:

1、一个对象必须实现__aiter__方法,该方法返回一个异步迭代器(asynchronous iterator)对象。
2、一个异步迭代器对象必须实现__anext__方法,该方法返回一个awaitable类型的值。
3、为了停止迭代,__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 TARGET in ITER:
  BLOCK
else:
  BLOCK2

这在语义上等同于:

iter = (ITER)
iter = type(iter).__aiter__(iter)
running = True
while running:
  try:
    TARGET = await type(iter).__anext__(iter)
  except StopAsyncIteration:
    running = False
  else:
    BLOCK
else:
  BLOCK2

把一个没有__aiter__方法的迭代对象传递给 async for将引起TypeError。如果在async def函数外面使用async with,将引起一个SyntaxError(语法错误)。

和常规的for表达式一样, async for也有一个可选的else 分句。.

例子1

使用异步迭代器能够在迭代过程中异步地缓存数据:

async for data in cursor:
  ...

这里的cursor是一个异步迭代器,能够从一个数据库中每经过N次迭代预取N行数据。

下面的语法展示了这种新的异步迭代协议的用法:

class Cursor:
  def __init__(self):
    self.buffer = collections.deque()

  async def _prefetch(self):
    ...

  def __aiter__(self):
    return self

  async def __anext__(self):
    if not self.buffer:
      self.buffer = await self._prefetch()
      if not self.buffer:
        raise StopAsyncIteration
    return self.buffer.popleft()

接下来这个Cursor 类可以这样使用:

async for row in Cursor():
  print(row)
which would be equivalent to the following code:

i = Cursor().__aiter__()
while True:
  try:
    row = await i.__anext__()
  except StopAsyncIteration:
    break
  else:
    print(row)

例子2

下面的代码可以将常规的迭代对象变成异步迭代对象。尽管这不是一个非常有用的东西,但这段代码说明了常规迭代器和异步迭代器之间的关系。

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)

为什么要抛出StopAsyncIteration?

协程(Coroutines)内部仍然是基于生成器的。因此在PEP 479之前,下面两种写法没有本质的区别:

def g1():
  yield from fut
  return 'spam'

def g2():
  yield from fut
  raise StopIteration('spam')

自从 PEP 479 得到接受并成为协程 的默认实现,下面这个例子将StopIteration包装成一个RuntimeError。

async def a1():
  await fut
  raise StopIteration('spam')

告知外围代码迭代已经结束的唯一方法就是抛出StopIteration。因此加入了一个新的异常类StopAsyncIteration。

由PEP 479的规定 , 所有协程中抛出的StopIteration异常都被包装在RuntimeError中。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python获得文件创建时间和修改时间的方法
Jun 30 Python
Python学习笔记之解析json的方法分析
Apr 21 Python
使用python将图片格式转换为ico格式的示例
Oct 22 Python
Python中文编码知识点
Feb 18 Python
python实现美团订单推送到测试环境,提供便利操作示例
Aug 09 Python
关于Python形参打包与解包小技巧分享
Aug 24 Python
18个Python脚本可加速你的编码速度(提示和技巧)
Oct 17 Python
python 判断txt每行内容中是否包含子串并重新写入保存的实例
Mar 12 Python
解决Jupyter Notebook开始菜单栏Anaconda下消失的问题
Apr 13 Python
python中加背景音乐如何操作
Jul 19 Python
Python colormap库的安装和使用详情
Oct 06 Python
python开发一款翻译工具
Oct 10 Python
python aiohttp的使用详解
Jun 20 #Python
Python 中Django验证码功能的实现代码
Jun 20 #Python
Puppeteer使用示例详解
Jun 20 #Python
获取django框架orm query执行的sql语句实现方法分析
Jun 20 #Python
Python使用LDAP做用户认证的方法
Jun 20 #Python
Python OpenCV中的resize()函数的使用
Jun 20 #Python
python中的句柄操作的方法示例
Jun 20 #Python
You might like
DW中链接mysql数据库时,建立字符集中文出现乱码的解决方法
2010/03/27 PHP
在PHP中运行Linux命令并启动SSH服务的例子
2014/06/12 PHP
PHPExcel内存泄漏问题解决方法
2015/01/23 PHP
地震发生中逃生十大法则
2008/05/12 Javascript
Javascript 赋值机制详解
2014/11/23 Javascript
javascript将异步校验表单改写为同步表单
2015/01/27 Javascript
JavaScript的原型继承详解
2015/02/15 Javascript
javascript跨域总结之window.name实现的跨域数据传输
2015/11/01 Javascript
轻松掌握JavaScript代理模式
2016/08/26 Javascript
对称加密与非对称加密优缺点详解
2017/02/06 Javascript
AngularJS学习笔记之表单验证功能实例详解
2017/07/06 Javascript
js is_valid_filename验证文件名的函数
2017/07/19 Javascript
让你彻底掌握es6 Promise的八段代码
2017/07/26 Javascript
基于webpack4搭建的react项目框架的方法
2018/06/30 Javascript
解决vue做详情页跳转的时候使用created方法 数据不会更新问题
2020/07/24 Javascript
H5 js点击按钮复制文本到粘贴板
2020/11/19 Javascript
[01:52]深扒TI7聊天轮盘语音出处7
2017/05/11 DOTA
Python实现字符串格式化输出的方法详解
2017/09/20 Python
Python自动化运维之IP地址处理模块详解
2017/12/10 Python
numpy的文件存储.npy .npz 文件详解
2018/07/09 Python
Python爬取知乎图片代码实现解析
2019/09/17 Python
Django框架model模型对象验证实现方法分析
2019/10/02 Python
python GUI库图形界面开发之PyQt5下拉列表框控件QComboBox详细使用方法与实例
2020/02/27 Python
python如何查看安装了的模块
2020/06/23 Python
Manuka Doctor美国官网:麦卢卡蜂蜜和蜂毒护肤
2016/12/25 全球购物
香港时尚女装购物网站:ZAFUL
2017/07/19 全球购物
美国女孩洋娃娃店:American Girl
2017/10/24 全球购物
汽车工程专业应届生求职信
2013/10/19 职场文书
学生学习总结的自我评价
2013/10/22 职场文书
幼儿园课题实施方案
2014/05/14 职场文书
放飞理想演讲稿
2014/09/09 职场文书
廉洁自律承诺书2015
2015/01/22 职场文书
2015年学生会部门工作总结
2015/04/21 职场文书
2015年度保密工作总结
2015/04/24 职场文书
六一儿童节致辞稿(3篇)
2019/07/11 职场文书
Vue组件更新数据v-model不生效的解决
2022/04/02 Vue.js