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中装饰器的一个妙用
Feb 08 Python
rabbitmq(中间消息代理)在python中的使用详解
Dec 14 Python
flask入门之表单的实现
Jul 18 Python
Python字符串的全排列算法实例详解
Jan 07 Python
Python函数中不定长参数的写法
Feb 13 Python
django使用django-apscheduler 实现定时任务的例子
Jul 20 Python
Python使用matplotlib 画矩形的三种方式分析
Oct 31 Python
django的autoreload机制实现
Jun 03 Python
python3检查字典传入函数键是否齐全的实例
Jun 05 Python
Pycharm操作Git及GitHub的步骤详解
Oct 27 Python
Python Http请求json解析库用法解析
Nov 28 Python
解决Pyinstaller打包软件失败的一个坑
Mar 04 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
星际初学者游戏中永远要做的事
2020/03/04 星际争霸
使用 eAccelerator加速PHP代码的方法
2007/09/30 PHP
php中一个完整表单处理实现代码
2011/11/10 PHP
php+redis实现多台服务器内网存储session并读取示例
2017/01/12 PHP
Laravel 5+ .env环境配置文件详解
2020/04/06 PHP
为jquery.ui.dialog 增加“自动记住关闭时的位置”的功能
2009/11/24 Javascript
javascript相等运算符与等同运算符详细介绍
2013/11/09 Javascript
javascript页面上使用动态时间具体实现
2014/03/18 Javascript
使用变量动态设置js的属性名
2014/10/19 Javascript
jQuery中:file选择器用法实例
2015/01/04 Javascript
JS的数组迭代方法
2015/02/05 Javascript
javascript实现数独解法
2015/03/14 Javascript
jquery实现可点击伸缩与展开的菜单效果代码
2015/08/31 Javascript
JavaScript数组去重的五种方法
2015/11/05 Javascript
jQuery对象的链式操作用法分析
2016/05/10 Javascript
原生JS实现的多个彩色小球跟随鼠标移动动画效果示例
2018/02/01 Javascript
vuex实现购物车的增加减少移除
2020/06/28 Javascript
python自动化测试实例解析
2014/09/28 Python
使用Python设置tmpfs来加速项目的教程
2015/04/17 Python
python读取TXT到数组及列表去重后按原来顺序排序的方法
2015/06/26 Python
Python利用正则表达式实现计算器算法思路解析
2018/04/25 Python
PyQt5实现暗黑风格的计时器
2019/07/29 Python
python异常处理之try finally不报错的原因
2020/05/18 Python
Python中bisect的用法及示例详解
2020/07/20 Python
GNC健安喜官方海外旗舰店:美国著名保健品牌
2017/01/04 全球购物
Oasis服装官网:时尚女装在线
2020/07/09 全球购物
大学生自我评价怎样写好
2013/10/23 职场文书
预备党员思想汇报
2014/01/08 职场文书
致1500米运动员广播稿
2014/02/07 职场文书
竞选班长的演讲稿
2014/04/24 职场文书
环保建议书400字
2014/05/14 职场文书
高中生学习计划书
2014/09/15 职场文书
2014年高中生自我评价范文
2014/09/26 职场文书
自愿离婚协议书范本
2015/01/26 职场文书
工厂仓管员岗位职责
2015/04/01 职场文书
Python爬虫之用Xpath获取关键标签实现自动评论盖楼抽奖(二)
2021/06/07 Python