告别样板代码,用生成器优雅管理资源
在Python开发中,上下文管理器是管理资源(如文件、数据库连接)的利器。传统的实现方法需要创建一个类并定义__enter__和__exit__两个方法,但Python的contextlib模块提供了更简洁的解决方案——@contextmanager装饰器。
传统方式需要完整定义一个类,包含__enter__和__exit__方法:
class TraditionalManager:
def __enter__(self):
# 资源初始化
return resource
def __exit__(self, exc_type, exc_val, exc_tb):
# 资源清理
这种方式虽然可行,但代码冗余度高,尤其对简单资源管理来说显得笨重。
使用@contextmanager装饰器,可用生成器函数替代完整类:
from contextlib import contextmanager
@contextmanager
def simple_manager():
# 相当于__enter__部分的代码
resource = setup_resource()
try:
yield resource # 返回给as变量
finally:
# 相当于__exit__部分的代码
cleanup_resource()
核心机制解析
创建一个反转所有输出文本的上下文管理器:
from contextlib import contextmanager
import sys
@contextmanager
def looking_glass():
original_write = sys.stdout.write
# 定义反转函数
def reverse_write(text):
original_write(text[::-1])
# 替换标准输出
sys.stdout.write = reverse_write
try:
yield 'JABBERWOCKY' # 返回给as变量
except Exception as e:
print(f"Error occurred: {e}")
raise
finally:
# 确保恢复原始输出
sys.stdout.write = original_write
使用效果:
with looking_glass() as word:
print("Hello, Python!")
print(word)
# 输出:
# !nohtyP ,olleH
# YKCOWREBBAJ
使用@contextmanager时必须正确处理异常:
@contextmanager
def safe_manager():
resource = acquire()
try:
yield resource
except CustomError as e:
handle_error(e)
finally:
release(resource) # 必须确保资源释放
重要原则:
Martijn Pieters实现的原地文件重写上下文管理器是绝佳实践:
@contextmanager
def inplace(filename, mode='r', **kwargs):
# 创建备份
backup = filename + '.bak'
os.rename(filename, backup)
try:
with open(backup, mode, **kwargs) as infh:
with open(filename, 'w' + mode.replace('r', ''), **kwargs) as outfh:
yield (infh, outfh) # 同时提供读写句柄
except:
# 出错时恢复备份
os.rename(backup, filename)
raise
finally:
# 成功完成删除备份
os.remove(backup)
使用示例:
with inplace('data.csv', 'r') as (infh, outfh):
reader = csv.reader(infh)
writer = csv.writer(outfh)
for row in reader:
if validate(row):
row.append('valid')
writer.writerow(row)
这个管理器实现了原子写入:要么成功更新文件,要么保留原文件不变。
特性 | 类实现 | @contextmanager |
---|---|---|
代码量 | 多 少 | (减少40%-60%) |
可读性 | 中等 | 高 |
异常处理 | 显式控制 | 需手动配置 |
适用场景 | 复杂逻辑 | 简单到中等逻辑 |
# 安全模板
@contextmanager
def template_manager():
setup_resources()
try:
yield resource
except SpecificError:
handle_error()
finally:
cleanup_resources() # 关键清理步骤 c
掌握此工具可显著提升代码简洁性和可维护性,但需警惕其异常处理机制的差异,避免资源泄漏隐患。在简单资源管理场景下,它是传统方法的完美替代品。