<摘要>

生产环境里,很多“文件句柄耗尽”或“数据库连接池爆满”的事故,

并不是代码逻辑太复杂,而是异常发生时资源没有正确释放,

平时代码里,这类问题占了相当大的比例。

Python 中处理这类问题最简单有效的方式,就是用 with 语句,

它让资源自动管理,不容易出错。

1. 最容易出问题的写法

f = open('data.txt', 'w')f.write('Hello World')# 如果这行代码出错了,下面的 close 永远不会执行!do_something_complex()f.close()

文件没关,句柄一直占用,时间长了系统就会报错。

2. 加上 try-finally 的写法

f = open('data.txt', 'w')try:f.write('Hello World')do_something_complex()finally:# 无论是否报错,这里都会执行f.close()

这样即使出异常,文件也会关闭。安全,但代码多几行,缩进也多,写起来不方便。

3. 用 with 的写法

withopen('data.txt', 'w') as f:f.write('Hello World')do_something_complex()# 出了缩进块,文件自动关闭。哪怕报错,也自动关闭。

with 块结束时,文件自动关闭。正常结束会关,抛异常也会关。代码短,逻辑清楚。

4、with 是怎么实现的

为什么 with 这么聪明?它到底在背后干了什么?

通俗点说,with 语句通过一种协议,强制对象必须遵守“进门登记”和“出门销户”的规则。

这涉及到两个特殊方法:

  • __enter__:进入 with 块时调用,通常用来获取资源(打开文件、连接数据库等)。
  • __exit__:离开 with 块时调用(不管正常结束还是异常),通常用来释放资源(关闭文件、断开连接等)。

你可以把它想象成酒店的“插卡取电”系统:

你进房间(Enter),插卡,通电;

你离开房间(Exit),拔卡,断电。

只要你人不在房间了,电一定会断。 这就是确定性。

5、 同时处理多个资源(Python 3.10+ 的写法)

在以前,如果你需要同时打开两个文件(比如把 A 文件内容读出来写入 B 文件),你可能会写嵌套:

# 旧时代的写法with open('input.txt') as fin:with open('output.txt', 'w') as fout:fout.write(fin.read())

这不仅难看,而且随着层级增加,代码会不断向右平移。

Python 3.10 引入了“带括号的上下文管理器” (Parenthesized context managers)。

# Python 3.10+ 新潮写法with (open('input.txt', 'r') as fin,open('output.txt', 'w') as foutfout.write(fin.read())

代码更平整,好读。建议用新写法。

6、 避坑指南

with 不创建作用域!

很多类似 C++ 或 Java 的语言,花括号 {} 内定义的变量在外部无法访问。但 Python 的 with 不同。所以变量名要避免和外面冲突。

with open('test.txt') as f:content = "Secret Data"print(content)  # 竟然能打印出来!输出:Secret Dataprint(f)        # 文件对象也还在!(虽然已经处于 closed 状态)