Python by Structure:上下文管理器与 with 语句
发布: (2025年12月7日 GMT+8 15:03)
4 min read
原文: Dev.to
Source: Dev.to
手动文件处理的问题
Timothy 在调试一个文件处理脚本时,Margaret 在日志中注意到一个 “打开的文件过多” 错误。
def process_config():
f = open('config.txt', 'r', encoding='utf-8')
data = f.read()
# Process data...
if 'error' in data:
return None # Oops – file never closed!
f.close()
return data
如果走到早期的 return,文件将保持打开状态。依赖垃圾回收不可靠,且可能耗尽系统资源。
传统解决方案:try/finally
def process_config():
f = open('config.txt', 'r', encoding='utf-8')
try:
data = f.read()
if 'error' in data:
return None
return data
finally:
f.close()
finally 块保证无论函数如何退出,f.close() 都会执行,但额外的样板代码令人烦恼。
with 语句
Python 的 with 语句自动完成清理工作:
def process_config():
with open('config.txt', 'r', encoding='utf-8') as f:
data = f.read()
if 'error' in data:
return None
return data
# File is automatically closed here
with 块有明确的入口点(打开文件)和退出点(缩进块结束)。Python 保证当块被离开时文件会被关闭,无论是正常执行、提前 return,还是抛出异常。
with 的工作原理:上下文管理器
在 with 语句中使用的对象必须实现两个特殊方法:
__enter__(self): 在块开始时调用;其返回值绑定到as后面的变量。__exit__(self, exc_type, exc_val, exc_tb): 在块结束时调用;如果抛出了异常,会收到异常信息。
简单的上下文管理器示例
class FileLogger:
def __init__(self, filename):
self.filename = filename
self.file = None
def __enter__(self):
print(f"Opening {self.filename}")
self.file = open(self.filename, 'w', encoding='utf-8')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"Closing {self.filename}")
if self.file:
self.file.close()
return False # Do not suppress exceptions
# Usage
with FileLogger('output.log') as log:
log.write('Starting process\n')
log.write('Processing data\n')
# File automatically closed here
输出
Opening output.log
Closing output.log
__exit__ 中的异常处理
如果在 with 块内部出现异常,Python 会把异常细节传递给 __exit__。该方法可以通过返回 True 来决定是否抑制异常。
class ErrorLogger:
def __enter__(self):
print("Entering context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
print(f"Exception occurred: {exc_type.__name__}: {exc_val}")
return True # Suppress the exception
print("Exiting normally")
return False
with ErrorLogger():
print("Working...")
raise ValueError("Something went wrong!")
print("Continuing after exception")
输出
Entering context
Working...
Exception occurred: ValueError: Something went wrong!
Continuing after exception
使用多个 with 语句
可以嵌套 with 语句,或在单行中组合使用(Python 3.1+)。
# Nested
with open('input.txt', 'r', encoding='utf-8') as infile:
with open('output.txt', 'w', encoding='utf-8') as outfile:
outfile.write(infile.read())
# Combined
with open('input.txt', 'r', encoding='utf-8') as infile, \
open('output.txt', 'w', encoding='utf-8') as outfile:
outfile.write(infile.read())
两个文件都保证被关闭,关闭顺序与获取顺序相反(后打开的先关闭)。
上下文管理器的常见用例
- 文件操作 – 保证关闭
- 数据库连接 – 保证提交/回滚
- 锁和信号量 – 保证释放
- 网络连接 – 保证断开
- 临时状态更改 – 保证恢复
- 任何需要清理的资源
示例:线程锁
import threading
lock = threading.Lock()
# Without context manager – risky
lock.acquire()
try:
# Critical section
pass
finally:
lock.release()
# With context manager – safe
with lock:
# Critical section
pass
即使在关键区块内部抛出异常,锁也会自动释放。