Python 上下文管理器的使用:contextmanager、asynccontextmanager

编程时,我们打开文件后常常忘了关它,这些“善后”工作,由称作为“上下文管理”来处理,程序员就少操好多心。

上下文管理器的使用

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)将是更好的选择。这样可以使你的代码更加清晰、易于维护,并且减少了重复代码的可能性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值