3.4. 协程

3.4.1. 可等待对象

awaitable 对象主要实现了 await() 方法。 从 async def 函数返回的 Coroutine 对象即属于可等待对象。

注解

从带有 types.coroutine()asyncio.coroutine() 装饰器的生成器返回的 generator iterator 对象也属于可等待对象,但它们并未实现 await()

3.5 新版功能.

参见

PEP 492 了解有关可等待对象的详细信息。

3.4.2. 协程对象

Coroutine 对象属于 awaitable 对象。 协程的执行可通过调用 await() 并迭代其结果来进行控制。 当协程结束执行并返回时,迭代器会引发 StopIteration,该异常的 value 属性将指向返回值。 如果协程引发了异常,它会被迭代器所传播。 协程不应该直接引发未处理的 StopIteration 异常。

协程也具有下面列出的方法,它们类似于生成器的对应方法 (参见 生成器-迭代器的方法)。 但是,与生成器不同,协程并不直接支持迭代。

在 3.5.2 版更改: 等待一个协程超过一次将引发 RuntimeError

  • coroutine.send(value)
  • 开始或恢复协程的执行。 如果 valueNone,则这相当于前往 await() 所返回迭代器的下一项。 如果 value 不为 None,此方法将委托给导致协程挂起的迭代器的 send() 方法。 其结果(返回值,StopIteration 或是其他异常)将与上述对 await() 返回值进行迭代的结果相同。
  • coroutine.throw(type[, value[, traceback]])
  • 在协程内引发指定的异常。 此方法将委托给导致协程挂起的迭代器的 throw() 方法,如果存在该方法。 否则的话,异常会在挂起点被引发。 其结果(返回值,StopIteration 或是其他异常)将与上述对 await() 返回值进行迭代的结果相同。 如果异常未在协程内被捕获,则将回传给调用者。
  • coroutine.close()
  • 此方法会使得协程清理自身并退出。 如果协程被挂起,此方法会先委托给导致协程挂起的迭代器的 close() 方法,如果存在该方法。 然后它会在挂起点引发 GeneratorExit,使得协程立即清理自身。 最后,协程会被标记为已结束执行,即使它根本未被启动。

当协程对象将要被销毁时,会使用以上处理过程来自动关闭。

3.4.3. 异步迭代器

异步迭代器 可以在其 anext 方法中调用异步代码。

异步迭代器可在 async for 语句中使用。

  • object.aiter(self)
  • 必须返回一个 异步迭代器 对象。
  • object.anext(self)
  • 必须返回一个 可迭代对象 输出迭代器的下一结果值。 当迭代结束时应该引发 StopAsyncIteration 错误。

异步可迭代对象的一个示例:

  1. class Reader:
  2. async def readline(self):
  3. ...
  4.  
  5. def __aiter__(self):
  6. return self
  7.  
  8. async def __anext__(self):
  9. val = await self.readline()
  10. if val == b'':
  11. raise StopAsyncIteration
  12. return val

3.5 新版功能.

在 3.7 版更改: 在 Python 3.7 之前,aiter 可以返回一个 可迭代对象 并解析为 异步迭代器

从 Python 3.7 开始,aiter 必须 返回一个异步迭代器对象。 返回任何其他对象都将导致 TypeError 错误。

3.4.4. 异步上下文管理器

异步上下文管理器上下文管理器 的一种,它能够在其 aenteraexit 方法中暂停执行。

异步上下文管理器可在 async with 语句中使用。

  • object.aenter(self)
  • 此方法在语义上类似于 enter(),仅有的区别是它必须返回一个 可等待对象
  • object.aexit(self, exc_type, exc_value, traceback)
  • 此方法在语义上类似于 exit(),仅有的区别是它必须返回一个 可等待对象

异步上下文管理器类的一个示例:

  1. class AsyncContextManager:
  2. async def __aenter__(self):
  3. await log('entering context')
  4.  
  5. async def __aexit__(self, exc_type, exc, tb):
  6. await log('exiting context')

3.5 新版功能.

备注

  • 1
  • 在某些情况下 有可能 基于可控的条件改变一个对象的类型。 但这通常不是个好主意,因为如果处理不当会导致一些非常怪异的行为。

  • 2

  • hash(), iter(), reversed() 以及 contains() 方法对此有特殊处理;其他方法仍会引发 TypeError,但可能依靠 None 属于不可调用对象的行为来做到这一点。

  • 3

  • 这里的“不支持”是指该类无此方法,或方法返回 NotImplemented。 如果你想强制回退到右操作数的反射方法,请不要设置方法为 None — 那会造成显式地 阻塞 此种回退的相反效果。

  • 4

  • 对于相同类型的操作数,如果非反射方法 (例如 add()) 失败则会认为相应运算不被支持,这就是反射方法未被调用的原因。