ZetCode

Python __await__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南探讨了 Python 的 __await__ 方法,这是一个使对象可等待的特殊方法。我们将介绍异步编程、协程、可等待对象以及实践示例。

基本定义

__await__ 方法返回一个迭代器,该迭代器用于实现可等待对象。它允许一个对象在异步函数中与 await 表达式一起使用。

主要特征:它必须返回一个迭代器,被 async/await 语法使用,并启用自定义的可等待对象。它是 Python 3.5 中引入的 Python 异步编程模型的一部分。

基本 __await__ 实现

这是一个简单的实现,展示了 __await__ 如何使自定义对象可等待。该方法必须返回一个迭代器。

basic_await.py
class SimpleAwaitable:
    def __await__(self):
        yield
        return "Done"

async def main():
    result = await SimpleAwaitable()
    print(result)  # Output: Done

import asyncio
asyncio.run(main())

此示例创建一个基本的可等待对象。__await__ 方法 yield 一次并返回一个值。当被 await 时,它会暂停执行,直到 yield 完成。

yield 至关重要 - 它使该方法成为生成器,这是 __await__ 所必需的。返回值成为 await 表达式的结果。

具有异步操作的可等待对象

此示例展示了一个更实用的可等待对象,它模拟了具有延迟的异步操作。

async_operation.py
class AsyncOperation:
    def __init__(self, delay):
        self.delay = delay
    
    def __await__(self):
        yield from asyncio.sleep(self.delay)
        return f"Completed after {self.delay}s"

async def main():
    op = AsyncOperation(1.5)
    print("Starting operation")
    result = await op
    print(result)

asyncio.run(main())

此可等待对象使用 asyncio.sleep 来模拟异步操作。yield from 委托给另一个可等待对象。

当被 await 时,它会在返回完成消息之前暂停执行指定的延迟。这种模式对于包装异步操作很常见。

自定义 Future 实现

此示例演示如何使用 __await__ 实现一个基本的类似 Future 的对象,该对象可以在以后设置结果。

custom_future.py
class CustomFuture:
    def __init__(self):
        self._result = None
        self._done = False
    
    def set_result(self, result):
        self._result = result
        self._done = True
    
    def __await__(self):
        while not self._done:
            yield
        return self._result

async def set_future(future, delay, value):
    await asyncio.sleep(delay)
    future.set_result(value)

async def main():
    future = CustomFuture()
    asyncio.create_task(set_future(future, 2, "Future result"))
    print("Waiting for future...")
    result = await future
    print(f"Got: {result}")

asyncio.run(main())

这个 CustomFuture 可以被 await,直到它的结果被设置。__await__ 方法 yield,直到 _done 变为 True,然后返回结果。

这模仿了真正的 Futures 在 asyncio 中的工作方式,允许解耦的结果设置和等待。此模式对于将基于回调的 API 与异步连接起来非常有用。

链式可等待对象

此示例展示了如何使用 __await__ 将多个可等待对象链接在一起。

chaining_awaitables.py
class AddAwaitable:
    def __init__(self, a, b):
        self.a = a
        self.b = b
    
    def __await__(self):
        result = yield from self.a.__await__()
        result += yield from self.b.__await__()
        return result

async def slow_add(a, b):
    await asyncio.sleep(1)
    return a + b

async def main():
    a = slow_add(10, 20)
    b = slow_add(30, 40)
    combined = AddAwaitable(a, b)
    result = await combined
    print(f"Total: {result}")  # Output: Total: 100

asyncio.run(main())

AddAwaitable 类链接了两个可等待对象,并将它们的结果相加。它使用 yield from 依次 await 每个操作数。

这演示了 __await__ 如何将多个异步操作组合成新的可等待对象。此模式对于构建复杂的异步工作流非常有用。

带有 __await__ 的异步上下文管理器

此示例使用 __await__ 实现了一个异步上下文管理器,用于资源管理。

async_context.py
class AsyncResource:
    def __init__(self, name):
        self.name = name
    
    async def __aenter__(self):
        print(f"Opening {self.name}")
        await asyncio.sleep(0.5)
        return self
    
    async def __aexit__(self, *args):
        print(f"Closing {self.name}")
        await asyncio.sleep(0.5)
    
    def __await__(self):
        return self.__aenter__().__await__()

async def main():
    async with AsyncResource("DB Connection") as resource:
        print(f"Using {resource.name}")
        await asyncio.sleep(1)
    
    # Alternative using await directly
    resource = await AsyncResource("File")
    try:
        print(f"Using {resource.name}")
        await asyncio.sleep(1)
    finally:
        await resource.__aexit__(None, None, None)

asyncio.run(main())

这展示了使用异步资源的两种方法:使用 async with 和直接使用 await__await__ 方法委托给 __aenter__

当您需要上下文管理器和直接 await 支持时,此模式很有用。它演示了 __await__ 如何与其他异步协议集成。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。到目前为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出所有 Python 教程