定义上下文管理器
python
import contextlib
@contextlib.contextmanager
def open_managed_file(filepath, mode='r'):
"""
一个上下文管理器,用于安全地打开和关闭文件。
"""
f = None
try:
# 1. 资源获取/进入上下文 (__enter__)
f = open(filepath, mode)
print(f"[ENTER]: 成功打开文件 '{filepath}',模式为 '{mode}'")
# 2. 暂停并返回资源
# yield 后面的值 (f) 将被绑定到 with ... as 后的变量上
yield f
except FileNotFoundError:
print(f"[EXIT]: 错误!文件 '{filepath}' 未找到。")
raise
finally:
# 3. 资源清理/退出上下文 (__exit__)
if f:
print(f"[EXIT]: 正在关闭文件 '{filepath}'...")
f.close()
使用 with 语句进行文件操作
我们首先创建一个临时文件供读取,然后使用上下文管理器。
python
# 1. 创建一个测试文件
TEST_FILE = "test_config.txt"
with open(TEST_FILE, "w") as temp:
temp.write("Setting A=10\n")
temp.write("Setting B=20\n")
print("-" * 30)
# 2. 使用上下文管理器读取文件
try:
with open_managed_file(TEST_FILE, 'r') as file_handle:
# 在 with 块内,file_handle 就是 open_managed_file 中 yield 返回的文件对象
content = file_handle.read()
print("\n[WITH BLOCK]: 文件内容读取完成。")
print("---")
print(content.strip())
print("---")
# 当 with 块结束时,文件已被自动关闭 (执行了 finally 块)
print("上下文管理块正常退出。")
except FileNotFoundError:
print("程序捕获到文件未找到的错误。")
运行结果(会显示整个流程):
yaml
------------------------------
[ENTER]: 成功打开文件 'test_config.txt',模式为 'r'
[WITH BLOCK]: 文件内容读取完成。
---
Setting A=10
Setting B=20
---
[EXIT]: 正在关闭文件 'test_config.txt'...
上下文管理块正常退出。
异常处理演示
即使在 with 块内部发生异常,finally 块也会确保文件被关闭:
python
print("-" * 30)
print("演示异常情况下的清理:")
try:
with open_managed_file(TEST_FILE, 'r') as file_handle:
print("[WITH BLOCK]: 尝试读取文件...")
# 故意制造一个错误(例如,除以零)
result = 10 / 0
print(f"这个不会被打印: {result}")
except ZeroDivisionError:
# 外部捕获了异常,但文件已经在退出时关闭了
print("\n[外部]: 成功捕获到 ZeroDivisionError。")
# 注意,即使发生了异常,open_managed_file 内部的 finally 块也会先执行。
异常运行结果(显示清理优先于异常捕获):
markdown
------------------------------
演示异常情况下的清理:
[ENTER]: 成功打开文件 'test_config.txt',模式为 'r'
[WITH BLOCK]: 尝试读取文件...
[EXIT]: 正在关闭文件 'test_config.txt'...
[外部]: 成功捕获到 ZeroDivisionError。
通过这种方式,我们使用了一个简洁的生成器函数 (open_managed_file),结合 @contextlib.contextmanager 和 try...finally 结构,优雅且安全地实现了上下文管理。