__enter__ 与 __exit__ 协议
使用双下划线协议构建上下文管理器。
__enter__ 与 __exit__ 协议 是 CoddyKit 上的免费 Python Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Python Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Python Academy 课程共包含 4 节课。
上下文管理器协议
任何定义了 __enter__ 和 __exit__ 的对象都是上下文管理器,都可以与 with 一起使用。
class Managed:
def __enter__(self):
print("entering")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("exiting")
return False
with Managed() as m:
print("inside")__enter__ 返回值
__enter__ 会在 with 代码块开始时调用。它的返回值会绑定到 as 后面的变量。
class Resource:
def __enter__(self):
self.value = 42
return self # bound to 'r'
def __exit__(self, *args):
self.value = None
return False
with Resource() as r:
print(r.value) # 42__exit__ 参数
如果发生错误,__exit__(exc_type, exc_val, exc_tb) 会接收异常信息。如果没有异常,三个参数都会是 None。
class Safe:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is ValueError:
print(f"Suppressed ValueError: {exc_val}")
return True # suppress the exception
return False # re-raise anything else
with Safe():
raise ValueError("oops") # suppressed抑制异常
从 __exit__ 返回 True 可以抑制异常。返回 False(或不返回任何值)则会让异常继续传播。
class Ignore:
def __enter__(self): return self
def __exit__(self, *args): return True # swallow all exceptions
with Ignore():
1 / 0
print("continues here")数据库连接示例
一个经典用例:进入时获取连接,退出时提交或回滚并关闭连接。
class DBConn:
def __enter__(self):
self.conn = connect_db()
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self.conn.rollback()
else:
self.conn.commit()
self.conn.close()
return False计时器上下文管理器
在 __enter__ 中记录开始时间,并在 __exit__ 中计算时间差,即可测量经过的时间。
import time
class Timer:
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, *args):
self.elapsed = time.perf_counter() - self.start
print(f"Elapsed: {self.elapsed:.3f}s")
return False
with Timer():
sum(range(1_000_000))嵌套上下文管理器
可以在同一行 with 语句中组合多个上下文管理器。
with open("in.txt") as src, open("out.txt", "w") as dst:
dst.write(src.read())可复用与一次性管理器
每次通过 __enter__ 分配新资源的上下文管理器是可复用的。会耗尽内部状态的管理器(例如只能持有一次的锁)可能无法复用。
class Reusable:
def __enter__(self):
return []
def __exit__(self, *a):
return False
for _ in range(3):
with Reusable() as lst:
lst.append(1)
print(lst) # [1] each time__aenter__ 与 __aexit__(异步)
对于异步上下文管理器,请定义 __aenter__ 和 __aexit__ 协程,并使用 async with。
class AsyncDB:
async def __aenter__(self):
self.conn = await connect()
return self.conn
async def __aexit__(self, *args):
await self.conn.close()
# async with AsyncDB() as conn:
# ...包装文件对象
Python 文件对象已经实现了上下文管理器协议——请不要手动调用 f.close()。
with open("data.txt", "r") as f:
data = f.read()
# file is closed here automatically
print(f.closed) # True实现 Lock
线程锁是上下文管理器:__enter__ 负责获取锁,__exit__ 负责释放锁。
import threading
lock = threading.Lock()
def update_shared():
with lock:
# critical section
pass快速检查
__exit__ 应返回什么来抑制异常?
总结
实现 __enter__ 和 __exit__ 来创建上下文管理器。在 __enter__ 中返回资源,在 __exit__ 中处理清理和异常抑制。
常见问题解答
「__enter__ 与 __exit__ 协议」课时是免费的吗?
是的 — 「__enter__ 与 __exit__ 协议」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Python Academy 课程的其余内容,请升级到 CoddyKit PRO。 Python Academy 课程共包含 4 节课。
「__enter__ 与 __exit__ 协议」这节课中我会学到什么?
使用双下划线协议构建上下文管理器。 你通过在浏览器中直接运行的动手代码来练习 Python Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Python Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Python Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「__enter__ 与 __exit__ 协议」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Python Academy 课中编写并运行代码吗?
能。每节 Python Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 深入理解 with 语句
- __enter__ 与 __exit__ 协议
- contextlib:@contextmanager
- 实用的上下文管理器模式