小白学 Python
课程GitHub
© 2026 小白学 Python · 基于 walter201230/Python 教程
课程目录GitHub
Python 环境加载中…
上下文管理器(with)0 / 4
1234

上下文管理器(with)

4 道练习题·预计 30 分钟·做对一题解锁下一段

教学 01 / 05· 已读

上下文管理器:with 到底在做什么

各位先来看一段代码,猜猜哪里有坑:

python
f = open('/tmp/diary.txt', 'w')
f.write('今天打卡迟到了三分钟。')
f.close()

「这有什么问题?文件打开了、写完了、关掉了,一气呵成。」善于思考的你可能会这么说。

可是,万一 f.write 那一行抛了异常呢?

close 永远不会被执行,文件描述符就这么悬在那儿。一两次没事,可如果是个 Web 服务跑一整天,几千个请求里只要有几十个写文件失败,操作系统的 fd 就会被慢慢耗光,最后报一个看起来八竿子打不着的「Too many open files」。这种 bug,定位起来非常痛苦。

那怎么办?老办法当然是 try/finally:

python
f = open('/tmp/diary.txt', 'w')
try:
    f.write('今天打卡迟到了三分钟。')
finally:
    f.close()

但这样很啰嗦。Python 早就提供了一个语法糖:

python
with open('/tmp/diary.txt', 'w') as f:
    f.write('今天打卡迟到了三分钟。')

一行顶六行,且保证不管中间是否抛异常,文件都会被关掉。

这就是 with 语句——它背后的机制叫上下文管理器(context manager)。

教学 02 / 05

一、上下文管理协议

with 后面跟的不是「文件」也不是「连接」,而是一类东西,Python 给它起了个名字,叫上下文管理器。

什么样的对象能算上下文管理器?只要满足两个方法:

  • __enter__(self):进入 with 块时被调用,返回值赋给 as 后面的变量
  • __exit__(self, exc_type, exc_val, tb):离开 with 块时被调用——无论是正常离开还是异常离开

这就是「上下文管理协议」。它的全部规则就这两条,没有第三条。

一个最简单的例子

python
class Punch:
    def __enter__(self):
        print('进入打卡区')
        return self

    def __exit__(self, exc_type, exc_val, tb):
        print('离开打卡区')


with Punch() as p:
    print('正在工位摸鱼')

输出:

进入打卡区
正在工位摸鱼
离开打卡区

with Punch() as p 这一行做了三件事:

  1. 创建一个 Punch() 实例
  2. 调用这个实例的 __enter__() 方法
  3. 把 __enter__() 的返回值赋给 p

然后才执行 with 块里面的代码。等 with 块结束(不管是正常结束还是异常结束),就调用 __exit__()。

这个流程本质上就是把:

python
p_obj = Punch()
p = p_obj.__enter__()
try:
    print('正在工位摸鱼')
finally:
    p_obj.__exit__(None, None, None)

这一坨样板代码,藏到了 with 这个语法糖背后。

练习 1 / 4·用 with 操作 StringIO题目有问题?

io.StringIO 是一个「内存里的文件」,也实现了上下文管理器协议。

请用 with 配合 io.StringIO():

  1. 在 with 块里 write 字符串 'Hello'
  2. 在 with 块里再 write 字符串 ' World'
  3. 把 getvalue() 的结果(Hello World)赋给变量 text
  4. print(text)

输出应该是:

Hello World
main.py
可编辑
🔒做对当前题解锁下一段 ·0/4
本章目录

上下文管理器(with)

  1. 教学 01上下文管理器:with 到底在做什么
  2. 教学 02一、上下文管理协议
  3. 练习 1用 with 操作 StringIO
  4. 教学 03二、自己写一个上下文管理器
  5. 练习 2 🔒自定义上下文管理器类
  6. 教学 04三、@contextmanager:用生成器写上下文管理器
  7. 练习 3 🔒用 @contextmanager 写一个 banner
  8. 教学 05四、contextlib 里的几个常用工具
  9. 练习 4 🔒用 contextlib.suppress 忽略异常
← 上一章20 · dataclass
上下文管理器(with)
下一章 →22 · async / await