ZetCode

Python __aenter__ 方法

最后修改于 2025 年 4 月 8 日

这篇全面的指南探索了Python的__aenter__方法,即异步上下文管理器的入口点。我们将涵盖基本用法、资源管理、错误处理和实用的异步模式。

基本定义

__aenter__方法是Python异步上下文管理器协议的一部分。 它定义了async with块的入口点,并且必须是一个异步函数。

主要特征:它在进入异步上下文时被调用,可以执行异步设置操作,并返回一个值,该值在as子句中可用。它与__aexit__协同工作进行清理。

基本异步上下文管理器

这是一个简单的实现,展示了__aenter__如何与__aexit__协同工作来管理异步资源。 这演示了基本协议。

basic_aenter.py
import asyncio

class AsyncContext:
    async def __aenter__(self):
        print("Entering context")
        await asyncio.sleep(0.1)
        return self
    
    async def __aexit__(self, exc_type, exc, tb):
        print("Exiting context")
        await asyncio.sleep(0.1)

async def main():
    async with AsyncContext() as ctx:
        print("Inside context")

asyncio.run(main())

此示例显示了异步上下文管理器的生命周期。 __aenter__在开始时被调用,然后执行主体,__aexit__处理清理。 所有方法都是协程。

即使发生异常,async with语句也能确保资源的正确获取和释放。 这种模式对于异步I/O至关重要。

数据库连接池

一个实际的用例是在异步应用程序中管理数据库连接。 __aenter__可以获取连接,而__aexit__释放连接。

db_pool.py
import asyncio
from typing import Optional

class DatabaseConnection:
    def __init__(self, pool):
        self.pool = pool
        self.conn: Optional[str] = None
    
    async def __aenter__(self):
        print("Acquiring connection from pool")
        await asyncio.sleep(0.2)  # Simulate connection delay
        self.conn = "connection-object"
        return self
    
    async def __aexit__(self, exc_type, exc, tb):
        print("Releasing connection back to pool")
        await asyncio.sleep(0.1)  # Simulate cleanup
        self.pool.release(self.conn)
        self.conn = None
    
    async def execute(self, query):
        print(f"Executing: {query}")
        return "result"

class ConnectionPool:
    async def get_connection(self):
        return DatabaseConnection(self)
    
    def release(self, conn):
        print(f"Released {conn}")

async def main():
    pool = ConnectionPool()
    async with await pool.get_connection() as conn:
        await conn.execute("SELECT * FROM users")

asyncio.run(main())

这种模式确保在异步应用程序中正确管理数据库连接。当上下文块结束时,即使发生错误,连接也会自动释放。

__aenter__方法处理连接获取,而__aexit__保证清理。这可以防止资源泄漏。

事务管理

__aenter__可以开始事务,而__aexit__根据是否发生异常来提交或回滚事务。

transaction.py
import asyncio

class Transaction:
    async def __aenter__(self):
        print("Starting transaction")
        await asyncio.sleep(0.1)
        return self
    
    async def __aexit__(self, exc_type, exc, tb):
        if exc_type is None:
            print("Committing transaction")
            await asyncio.sleep(0.1)
        else:
            print("Rolling back transaction")
            await asyncio.sleep(0.1)
    
    async def execute(self, query):
        print(f"Executing: {query}")
        return "result"

async def successful_operation():
    async with Transaction() as tx:
        await tx.execute("INSERT INTO users VALUES (1, 'John')")
        await tx.execute("UPDATE stats SET count = count + 1")

async def failing_operation():
    async with Transaction() as tx:
        await tx.execute("DELETE FROM users WHERE id = 1")
        raise ValueError("Something went wrong")

async def main():
    await successful_operation()
    try:
        await failing_operation()
    except ValueError:
        pass

asyncio.run(main())

此示例显示了异步上下文管理器如何处理数据库事务。 __aexit__方法检查异常以决定是提交还是回滚。

如果任何操作失败,事务将自动回滚。 这种模式确保异步应用程序中的数据一致性。

速率限制上下文

__aenter__可以通过在允许代码执行之前等待来实现速率限制。 这对于具有速率限制的API客户端很有用。

rate_limiter.py
import asyncio
import time

class RateLimiter:
    def __init__(self, calls_per_second):
        self.calls_per_second = calls_per_second
        self.last_call = 0
    
    async def __aenter__(self):
        now = time.time()
        delay = max(0, 1/self.calls_per_second - (now - self.last_call))
        if delay > 0:
            print(f"Waiting {delay:.2f}s to respect rate limit")
            await asyncio.sleep(delay)
        self.last_call = time.time()
        return self
    
    async def __aexit__(self, exc_type, exc, tb):
        pass

async def make_api_call(i):
    print(f"Making API call {i}")

async def main():
    async with RateLimiter(2):  # 2 calls per second max
        await make_api_call(1)
    async with RateLimiter(2):
        await make_api_call(2)
    async with RateLimiter(2):
        await make_api_call(3)  # Will be delayed

asyncio.run(main())

此速率限制器确保API调用不超过指定的速率。 __aenter__方法计算并等待所需的延迟。

上下文管理器跟踪上次调用时间并强制执行调用之间的最小延迟。 这可以防止达到API速率限制。

模拟异步资源

当模拟异步资源时,__aenter__对于测试非常有用。 此示例显示了用于单元测试的模拟数据库连接。

mock_db.py
import asyncio
from unittest.mock import AsyncMock

class MockDatabase:
    async def __aenter__(self):
        self.mock_conn = AsyncMock()
        self.mock_conn.execute.return_value = {"status": "ok"}
        return self.mock_conn
    
    async def __aexit__(self, exc_type, exc, tb):
        pass

async def query_database():
    async with MockDatabase() as conn:
        result = await conn.execute("SELECT 1")
        return result

async def main():
    result = await query_database()
    print("Query result:", result)

asyncio.run(main())

此模拟数据库在测试期间返回预定义的响应。 __aenter__方法使用预期的行为设置模拟连接。

可以将mock配置为返回不同的响应或引发异常以测试各种场景。 这有助于隔离测试异步代码。

最佳实践

资料来源

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程