使用
@asyncio.coroutine
可以编写一个细粒度的资源管理器来获取和释放共享资源(例如 Lock
或全局变量或数据库连接):
@asyncio.coroutine
def resource_context(func: Callable[..., Awaitable]):
print("acquire")
for i in func().__await__():
print("release")
yield i
print("acquire")
print("release")
这种细粒度的资源管理需要使用
yield
而不是 yield from
。这比上下文管理器的粒度要细得多,上下文管理器只会在开始时获取一次并在结束时释放一次。虽然上下文管理器会在我们的func
等待时持有资源,但这种更细粒度的资源会在每次等待时释放资源。
但是
@asyncio.coroutine
从 Python 3.8 开始被弃用。我如何使用更现代的 async def
协同程序实现此功能?
注意我不是在寻找异步迭代器或异步上下文管理器:这些都不适用于此用例。我希望能够等待在
func
内部调用的任意其他异步函数,并在这些被调用者等待任何事情时释放资源。也就是说,我希望在 func
和它调用的任何代码中获取资源,但在 func
放弃控制权时暂时释放。
这是一个可行的答案,但它并不漂亮:
class AwaitOne(typing.NamedTuple):
future: asyncio.Future
def __await__(self):
yield self.future
async def resource_context(coro: typing.Awaitable):
print("acquire")
for future in coro.__await__():
print("release")
await AwaitOne(future)
print("acquire")
print("release")
我认为这里发生的事情是新的
await
语法模拟的不是旧式yield
而是旧式yield from
。现在我们需要从可迭代的未来中产生收益,但我们只有一个未来。因此,让我们将其包装在长度为 1 的序列的 await
中。这个长度为一的容器只需要一个 __await__
方法来产生它的单一未来。