等待一个自定义类的对象会发生什么?

后端开发 2026-07-08

Python文档 指出:

await 是Python的一个关键字,通常以两种不同的方式使用:

await task await coroutine

在本质上,await 的行为取决于被等待对象的类型。

本节指出:

  1. 「coroutine」与「coroutine object」同义。
  2. 「coroutine object」通过调用「coroutine function」(也称为「异步函数」,这是同义词)来创建。

例如

async def loudmouth_penguin(magic_number: int):
    print(
     "I am a super special talking penguin. Far cooler than that printer. "
     f"By the way, my lucky number is: {magic_number}."
    )
obj = loudmouth_penguin(magic_number=3)

在这种情况下,loudmouth_penguin 是一个「coroutine function」(或「异步函数」),而 obj 是一个「coroutine object」(或「协程」)。

一个任务通过 asyncio.create_task 函数创建:

task = asyncio.create_task(coroutine)

回到 await

  • 等待一个任务会把控制权从当前任务或协程让渡给事件循环
  • 与任务不同,等待一个协程不会把控制权交还给事件循环!await coroutine 的行为本质上等同于调用一个普通的、同步的Python函数。

接着我们有这个示例(我稍作修改,增加了一些打印语句以便了解发生了什么):

class Rock:
    def __await__(self):
        print(f"\t\tRock.__await__ called")
        value_sent_in = yield 7
        print(f"\t\tRock.__await__ resuming with value: {value_sent_in}.")
        return value_sent_in

async def main():
    print(f"\tBeginning coroutine main().")
    rock = Rock()
    print(f"\tAwaiting rock...")
    value_from_rock = await rock
    print(f"\tCoroutine received value: {value_from_rock} from rock.")
    return 23

print(f"Calling coroutine")
coroutine = main()
intermediate_result = coroutine.send(None)
print(f"Coroutine paused and returned intermediate value: {intermediate_result}.\n")

print(f"Resuming coroutine and sending in value: 42.")
try:
    coroutine.send(42)
except StopIteration as e:
    returned_value = e.value
print(f"Coroutine main() finished and provided value: {returned_value}.")

运行它将返回:

Calling coroutine
    Beginning coroutine main().
    Awaiting rock...
        Rock.__await__ called
Coroutine paused and returned intermediate value: 7.

Resuming coroutine and sending in value: 42.
        Rock.__await__ resuming with value: 42.
    Coroutine received value: 42 from rock.
Coroutine main() finished and provided value: 23.

在这种情况下,我们以文档从未解释过的方式使用 await。我们正在等待一个对象,即一个用户自定义类的实例(第 value_from_rock = await rock 行)。

对一个用户自定义类的实例执行 await 会怎样?我们不知道。文档没有说明。用户只能自行猜测。

大概,它会立即调用该类的 __await__ 成员函数并返回?(因此不把控制权推回事件循环)

所以

value_from_rock = await rock

在本质上等同于:

value_from_rock = rock.__await__()
yield value_from_rock

请注意,在执行完

value_from_rock = await rock

之后,下一行

print(f"\tCoroutine received value: {value_from_rock} from rock.")

在第一次调用协程时并不会执行。就像它在立即返回一样。

解决方案

await 起源于 yield from,因此如果对象具备相应的方法,它与 yield from custom_object.__await__() 同义(原生对象,例如实际的协程对象,内部有用于此的隐藏方法)。

语法和协议的完整起源最好由 PEP 492 解释:

The following new await expression is used to obtain a result of coroutine execution:

async def read_data(db): data = await db.fetch('SELECT ...') ...

await, similarly to yield from, suspends execution of read_data coroutine until db.fetch awaitable completes and returns the result data.

It uses the yield from implementation with an extra step of validating its argument. await only accepts an awaitable, which can be one of:

  • A native coroutine object returned from a native coroutine function.
  • A generator-based coroutine object returned from a function decorated with types.coroutine().
  • An object with an __await__ method returning an iterator.

Any yield from chain of calls ends with a yield. This is a fundamental mechanism of how Futures are implemented. Since, internally, coroutines are a special kind of generators, every await is suspended by a yield somewhere down the chain of await calls (please refer to PEP 3156 for a detailed explanation).

To enable this behavior for coroutines, a new magic method called __await__ is added. In asyncio, for instance, to enable Future objects in await statements, the only change is to add __await__ = __iter__ line to asyncio.Future class.

Objects with __await__ method are called Future-like objects in the rest of this PEP.

It is a TypeError if __await__ returns anything but an iterator. * Objects defined with CPython C API with a tp_as_async.am_await function, returning an iterator (similar to __await__ method).

It is a SyntaxError to use await outside of an async def function (like it is a SyntaxError to use yield outside of def function).

It is a TypeError to pass anything other than an awaitable object to an await expression.

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章