编程时,我们打开文件后常常忘了关它,这些“善后”工作,由称作为“上下文管理”来处理,程序员就少操好多心。
上下文管理器的使用
1. 使用内置的上下文管理器
Python 中有许多内置的对象都支持上下文管理协议(即实现了 __enter__()
和 __exit__()
方法),比如文件对象。
with open('example.txt', 'r') as file:
content = file.read()
print(content)
# 文件在这里自动关闭
Python 的内置 open函数已经实现了上下文管理协议(即具有 __enter__和 __exit__方法),因此可以直接使用 with语句来自动管理文件的打开和关闭。
注意:要用with语句
2. 使用 contextmanager装饰器创建自定义上下文管理器
contextlib
是 Python 的一个标准库,它提供了一系列用于上下文管理(context management)的工具和装饰器,使得编写上下文管理器变得更加简单和灵活。上下文管理器主要用于资源的管理,如文件操作、网络连接、数据库事务等,确保资源在使用后能被正确关闭或释放,即使在发生异常时也是如此。
from contextlib import contextmanager
@contextmanager
def my_context_manager():
print('Entering the context')
try:
yield 'Something from the context'
finally:
print('Exiting the context')
with my_context_manager() as value:
print(value)
输出:
Entering the context
Something from the context
Exiting the context
3. 使用 asynccontextmanager装饰器创建异步上下文管理器
asynccontextmanager
的作用
在 Python 3.5 及以后的版本中,contextlib
库引入了 asynccontextmanager
装饰器,用于创建异步上下文管理器(asynchronous context managers)。异步上下文管理器主要用于异步编程中,管理那些需要异步释放的资源,比如异步数据库连接或异步文件操作。
from contextlib import asynccontextmanager
import asyncio
@asynccontextmanager
async def my_async_context_manager():
print('Entering the async context')
try:
yield 'Something from the async context'
finally:
print('Exiting the async context')
async def main():
async with my_async_context_manager() as value:
print(value)
asyncio.run(main())
输出:
Entering the async context
Something from the async context
Exiting the async context
contextlib
库通过提供 contextmanager
和 asynccontextmanager
装饰器,极大地简化了上下文管理器的编写和使用,无论是同步还是异步编程中。这些工具使得资源管理变得更加安全和方便,减少了资源泄露的风险。
考考你:如下程序有错么?
import asyncio
async def my_async():
print('Entering ')
try:
yield 'do Something '
finally:
print('Exiting ')
async def main():
async with my_async() as value:
print(value)
asyncio.run(main())
问题:
1、在于 my_async 函数被定义为一个异步函数(使用了 async def),但它内部却使用了 yield 而不是 await。在异步函数中,你应该使用 await 来等待异步操作完成,而不是 yield。
2、async with
语句期望的是一个实现了异步上下文管理协议的对象,即一个具有 __aenter__
和 __aexit__
方法的对象。
修改1:作为普通的异步函数
import asyncio
async def my_async_function():
print('Entering the async function')
# 这里可以放置一些初始化代码或资源获取
# 假设这是函数的“主体”,我们在这里“做点什么”
result = 'do Something'
# 在函数返回之前,执行清理或资源释放的代码
# 这相当于异步上下文管理器中的 finally 块
print('Exiting the async function')
# 返回结果
return result
async def main():
# 直接调用异步函数,并处理其返回值
value = await my_async_function()
print(value)
# 运行主函数
asyncio.run(main())
配合的是await
修改2:使用异步上下文管理器
使用 @asynccontextmanager
来正确实现异步上下文管理器(一个具 __aenter__
和 __aexit__
方法的对象),与此配合的是 async with
语句:
import asyncio
from contextlib import asynccontextmanager
@asynccontextmanager
async def my_async_context_manager():
print('Entering ')
try:
yield 'do Something '
finally:
print('Exiting ')
async def main():
async with my_async_context_manager() as value:
print(value)
asyncio.run(main())
如果你确实需要在多个地方使用相同的进入和退出逻辑,并且希望这些逻辑能够自动管理(比如自动调用清理代码),那么使用异步上下文管理器(通过 @asynccontextmanager
)将是更好的选择。这样可以使你的代码更加清晰、易于维护,并且减少了重复代码的可能性。