跳转至

18. 异步上下文管理器

18. Asynchronous Context Managers

上下文管理器是一个 Python 结构,它提供了一个类似 try-finally 的环境,具有一致的接口和方便的语法,例如 通过“with”表达。

它通常与资源一起使用,确保资源在使用完毕后始终关闭或释放,无论资源的使用是否成功或因异常而失败。

Asyncio 允许我们开发异步上下文管理器。

我们可以通过定义一个实现 __aenter__()__aexit__() 方法的对象作为协程来在 asyncio 程序中创建和使用异步上下文管理器。

让我们仔细看看。

A context manager is a Python construct that provides a try-finally like environment with a consistent interface and handy syntax, e.g. via the “with” expression.

It is commonly used with resources, ensuring the resource is always closed or released after we are finished with it, regardless of whether the usage of the resources was successful or failed with an exception.

Asyncio allows us to develop asynchronous context managers.

We can create and use asynchronous context managers in asyncio programs by defining an object that implements the __aenter__() and __aexit__() methods as coroutines.

Let’s take a closer look.

18.1 什么是异步上下文管理器

18.1 What is an Asynchronous Context Manager

异步上下文管理器是一个实现 __aenter__()__aexit__() 方法的 Python 对象。

在我们深入了解异步上下文管理器的细节之前,让我们回顾一下经典的上下文管理器。

An asynchronous context manager is a Python object that implements the __aenter__() and __aexit_ _() methods.

Before we dive into the details of asynchronous context managers, let’s review classical context managers.

18.1.1 上下文管理器

18.1.1 Context Manager

上下文管理器是一个实现 __enter__() 和 __exit__() 方法的 Python 对象。

上下文管理器是一个对象,它定义执行 with 语句时要建立的运行时上下文。 上下文管理器处理执行代码块所需的运行时上下文的进入和退出。

WITH STATEMENT CONTEXT MANAGERS

  • __enter__() 方法定义在块开始时发生的情况,例如打开或准备资源,如文件、套接字或线程池。
  • __exit__() 方法定义退出块时会发生什么,例如关闭准备好的资源。

上下文管理器的典型用途包括保存和恢复各种全局状态、锁定和解锁资源、关闭打开的文件等。

WITH STATEMENT CONTEXT MANAGERS

上下文管理器通过“with”表达式使用。

通常,上下文管理器对象是在“with”表达式的开头创建的,并且自动调用 __enter__() 方法。 内容的主体通过指定的上下文管理器对象使用资源,然后当块正常或通过异常退出时,自动调用 __aexit__() 方法。

例如:

...
# 打开上下文管理器
with ContextManager() as manager:
    # ...
# 自动关闭
This mirrors a try-finally expression.

例如:

...
# 创建对象
manager = ContextManager()
try:
    manager.__enter__()
    # ...
finally:
    manager.__exit__()

接下来,让我们看一下异步上下文管理器。

A context manager is a Python object that implements the __enter__() and __exit__() methods.

A context manager is an object that defines the runtime context to be established when executing a with statement. The context manager handles the entry into, and the exit from, the desired runtime context for the execution of the block of code.

WITH STATEMENT CONTEXT MANAGERS

  • The __enter__() method defines what happens at the beginning of a block, such as opening or preparing resources, like a file, socket or thread pool.
  • The __exit__() method defines what happens when the block is exited, such as closing a prepared resource.

Typical uses of context managers include saving and restoring various kinds of global state, locking and unlocking resources, closing opened files, etc.

WITH STATEMENT CONTEXT MANAGERS

A context manager is used via the “with” expression.

Typically the context manager object is created in the beginning of the “with” expression and the __enter__() method is called automatically. The body of the content makes use of the resource via the named context manager object, then the __aexit__() method is called automatically when the block is exited, normally or via an exception.

For example:

...
# open a context manager
with ContextManager() as manager:
    # ...
# closed automatically
This mirrors a try-finally expression.

For example:

...
# create the object
manager = ContextManager()
try:
    manager.__enter__()
    # ...
finally:
    manager.__exit__()

Next, let’s take a look at asynchronous context managers.

18.1.2 异步上下文管理器

18.1.2 Asynchronous Context Manager

异步上下文管理器在“PEP 492 – 具有异步和等待语法的协程”中引入。

它们提供了一个上下文管理器,可以在进入和退出时暂停。

异步上下文管理器是能够在其 __aenter__ 和 __aexit__ 方法中暂停执行的上下文管理器。

ASYNCHRONOUS CONTEXT MANAGERS

__aenter____aexit__ 方法被定义为协程并由调用者等待。

这是使用“async with”表达式来实现的。

您可以在教程中了解有关“async with”表达式的更多信息:

因此,异步上下文管理器只能在 asyncio 程序中使用,例如在调用协程中。

什么是“异步”

async with”表达式用于创建和使用异步上下文管理器。

它是“with”表达式的扩展,用于 asyncio 程序中的协程。

async with”表达式就像用于上下文管理器的“with”表达式一样,只不过它允许在协程中使用异步上下文管理器。

为了更好地理解“async with”,让我们仔细看看异步上下文管理器。

async with 表达式允许协程创建和使用上下文管理器的异步版本。

例如:

...
# 创建并使用异步上下文管理器
async with AsyncContextManager() as manager:
    # ...

这相当于:

...
# 创建或进入异步上下文管理器
manager = await AsyncContextManager()
try:
    # ...
finally:
    # 关闭或退出上下文管理器
    await manager.close()

请注意,我们实现的模式与传统上下文管理器几乎相同,只是创建和关闭上下文管理器涉及等待协程。

这会暂停当前协程的执行,安排一个新的协程并等待其完成。

因此,异步上下文管理器必须实现 __aenter__()__aexit__() 方法,这些方法必须通过 async def 表达式定义。 这使得它们本身成为协程,也可能等待。

Asynchronous context managers were introduced in “PEP 492 – Coroutines with async and await syntax“.

They provide a context manager that can be suspended when entering and exiting.

An asynchronous context manager is a context manager that is able to suspend execution in its __aenter__ and __aexit__ methods.

— ASYNCHRONOUS CONTEXT MANAGERS

The __aenter__ and __aexit__ methods are defined as coroutines and are awaited by the caller.

This is achieved using the “async with” expression.

You can learn more about the “async with” expression in the tutorial:

As such, asynchronous context managers can only be used within asyncio programs, such as within calling coroutines.

What is “async with”

The “async with” expression is for creating and using asynchronous context managers.

It is an extension of the “with” expression for use in coroutines within asyncio programs.

The “async with” expression is just like the “with” expression used for context managers, except it allows asynchronous context managers to be used within coroutines.

In order to better understand “async with“, let’s take a closer look at asynchronous context managers.

The async with expression allows a coroutine to create and use an asynchronous version of a context manager.

For example:

...
# create and use an asynchronous context manager
async with AsyncContextManager() as manager:
    # ...

This is equivalent to something like:

...
# create or enter the async context manager
manager = await AsyncContextManager()
try:
    # ...
finally:
    # close or exit the context manager
    await manager.close()

Notice that we are implementing much the same pattern as a traditional context manager, except that creating and closing the context manager involve awaiting coroutines.

This suspends the execution of the current coroutine, schedules a new coroutine and waits for it to complete.

As such an asynchronous context manager must implement the __aenter__() and __aexit__() methods that must be defined via the async def expression. This makes them coroutines themselves which may also await.

18.2 如何使用异步上下文管理器

18.2 How to Use Asynchronous Context Managers

在本节中,我们将探讨如何在 asyncio 程序中定义、创建和使用异步上下文管理器。

In this section, we will explore how we can define, create, and use asynchronous context managers in our asyncio programs.

18.2.1 定义异步上下文管理器

18.2.1 Define an Asynchronous Context Manager

我们可以将异步上下文管理器定义为实现 __aenter__()__aexit__() 方法的 Python 对象。

重要的是,这两种方法都必须使用“async def”定义为协程,因此必须返回可等待对象。

例如:

# 定义异步上下文管理器
class AsyncContextManager:
    # 进入异步上下文管理器
    async def __aenter__(self):
        # 报告消息
        print('>entering the context manager')

    # 退出异步上下文管理器
    async def __aexit__(self, exc_type, exc, tb):
        # 报告消息
        print('>exiting the context manager')

因为每个方法都是协程,所以它们本身可能等待协程或任务。

例如:

# 定义异步上下文管理器
class AsyncContextManager:
    # 进入异步上下文管理器
    async def __aenter__(self):
        # 报告消息
        print('>entering the context manager')
        # 暂时阻塞
        await asyncio.sleep(0.5)

    # 退出异步上下文管理器
    async def __aexit__(self, exc_type, exc, tb):
        # 报告消息
        print('>exiting the context manager')
        # 暂时阻塞
        await asyncio.sleep(0.5)

We can define an asynchronous context manager as a Python object that implements the __aenter__() and __aexit__() methods.

Importantly, both methods must be defined as coroutines using the “async def” and therefore must return awaitables.

For example:

# define an asynchronous context manager
class AsyncContextManager:
    # enter the async context manager
    async def __aenter__(self):
        # report a message
        print('>entering the context manager')

    # exit the async context manager
    async def __aexit__(self, exc_type, exc, tb):
        # report a message
        print('>exiting the context manager')

Because each of the methods are coroutines, they may themselves await coroutines or tasks.

For example:

# define an asynchronous context manager
class AsyncContextManager:
    # enter the async context manager
    async def __aenter__(self):
        # report a message
        print('>entering the context manager')
        # block for a moment
        await asyncio.sleep(0.5)

    # exit the async context manager
    async def __aexit__(self, exc_type, exc, tb):
        # report a message
        print('>exiting the context manager')
        # block for a moment
        await asyncio.sleep(0.5)

18.2.2 使用异步上下文管理器

18.2.2 Use an Asynchronous Context Manager

异步上下文管理器通过“async with”表达式使用。

这将自动等待进入和退出协程,并根据需要暂停调用协程。

例如:

...
# 使用异步上下文管理器
async with AsyncContextManager() as manager:
    # ...

因此,“async with”表达式和异步上下文管理器更一般地只能在 asyncio 程序中使用,例如在协程中。

现在我们知道如何使用异步上下文管理器,让我们看一个有效的示例。

An asynchronous context manager is used via the “async with” expression.

This will automatically await the enter and exit coroutines, suspending the calling coroutine as needed.

For example:

...
# use an asynchronous context manager
async with AsyncContextManager() as manager:
    # ...

As such, the “async with” expression and asynchronous context managers more generally can only be used within asyncio programs, such as within coroutines.

Now that we know how to use asynchronous context managers, let’s look at a worked example.

18.3 异步上下文管理器和“async with”的示例

18.3 Example of an Asynchronous Context Manager and “async with”

我们可以通过“async with”表达式探索如何使用异步上下文管理器。

在此示例中,我们将更新上面的示例以正常方式使用上下文管理器。

我们将使用“async with”表达式,并在一行中创建并输入上下文管理器。 这将自动等待输入方法。

然后我们可以在内部块中使用管理器。 在这种情况下,我们只会报告一条消息。

退出内部块将自动等待上下文管理器的退出方法。

将此示例与前面的示例进行对比,可以看出“async with”表达式在 asyncio 程序中为我们带来了多大的负担。

下面列出了完整的示例。

# SuperFastPython.com
# 通过 async with 实现异步上下文管理器的示例
import asyncio

# 定义异步上下文管理器
class AsyncContextManager:
    # 进入异步上下文管理器
    async def __aenter__(self):
        # 报告消息
        print('>entering the context manager')
        # 暂时阻塞
        await asyncio.sleep(0.5)

    # 退出异步上下文管理器
    async def __aexit__(self, exc_type, exc, tb):
        # 报告消息
        print('>exiting the context manager')
        # 暂时阻塞
        await asyncio.sleep(0.5)

# 定义一个简单的协程
async def custom_coroutine():
    # 创建并使用异步上下文管理器
    async with AsyncContextManager() as manager:
        # 报告结果
        print(f'within the manager')

# 启动异步程序
asyncio.run(custom_coroutine())

运行该示例首先创建 main() 协程,并将其用作 asyncio 程序的入口点。

main() 协程运行并在“async with”表达式中创建 AsyncContextManager 类的实例。

该表达式自动调用 Enter 方法并等待协程。 报告一条消息,协程阻塞片刻。

main() 协程恢复并执行上下文管理器的主体,打印一条消息。

该块退出并自动等待上下文管理器的退出方法,报告消息并休眠一会儿。

这突出显示了 asyncio 程序中异步上下文管理器的正常使用模式。

>entering the context manager
within the manager
>exiting the context manager

您可以在教程中了解有关异步上下文管理器的更多信息:

接下来,我们将探索异步推导式。

We can explore how to use an asynchronous context manager via the “async with” expression.

In this example, we will update the above example to use the context manager in a normal manner.

We will use an “async with” expression and on one line, create and enter the context manager. This will automatically await the enter method.

We can then make use of the manager within the inner block. In this case, we will just report a message.

Exiting the inner block will automatically await the exit method of the context manager.

Contrasting this example with the previous example shows how much heavy lifting the “async with” expression does for us in an asyncio program.

The complete example is listed below.

# SuperFastPython.com
# example of an asynchronous context manager via async with
import asyncio

# define an asynchronous context manager
class AsyncContextManager:
    # enter the async context manager
    async def __aenter__(self):
        # report a message
        print('>entering the context manager')
        # block for a moment
        await asyncio.sleep(0.5)

    # exit the async context manager
    async def __aexit__(self, exc_type, exc, tb):
        # report a message
        print('>exiting the context manager')
        # block for a moment
        await asyncio.sleep(0.5)

# define a simple coroutine
async def custom_coroutine():
    # create and use the asynchronous context manager
    async with AsyncContextManager() as manager:
        # report the result
        print(f'within the manager')

# start the asyncio program
asyncio.run(custom_coroutine())

Running the example first creates the main() coroutine and uses it as the entry point into the asyncio program.

The main() coroutine runs and creates an instance of our AsyncContextManager class in an “async with” expression.

This expression automatically calls the enter method and awaits the coroutine. A message is reported and the coroutine blocks for a moment.

The main() coroutine resumes and executes the body of the context manager, printing a message.

The block is exited and the exit method of the context manager is awaited automatically, reporting a message and sleeping a moment.

This highlights the normal usage pattern for an asynchronous context manager in an asyncio program.

>entering the context manager
within the manager
>exiting the context manager

You can learn more about async context managers in the tutorial:

Next, we will explore asynchronous comprehensions.


最后更新: 2024年9月4日
创建日期: 2024年9月4日