0Pricing
Python Academy · 课时

__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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 深入理解 with 语句
  2. __enter__ 与 __exit__ 协议
  3. contextlib:@contextmanager
  4. 实用的上下文管理器模式
← 返回 Python Academy