Python 的 with 语句可以用来管理资源的自动清理,并替代 try...finally 语句,使代码更简洁易读。
1. with 语句的作用
 
在 Python 里,with 语句通常用于管理资源,比如文件、数据库连接、网络请求等。
 它可以保证无论代码是否执行成功,都会执行必要的清理工作,比如自动关闭文件、断开连接等。
示例: 用 with 语句打开文件,自动关闭:
with open('foo.txt') as fp:content = fp.read()  # 读取文件内容
# 代码运行到这里,文件会自动关闭
 
在没有 with 的情况下,必须手动关闭文件:
fp = open('foo.txt')
try:content = fp.read()
finally:fp.close()  # 确保文件被关闭
 
对比可见,with 语句更简洁,不用显式调用 close()。
2. with 的底层原理——上下文管理器
 
with 之所以能自动管理资源,是因为它依赖上下文管理器(context manager)。
 上下文管理器是实现了 __enter__ 和 __exit__ 方法的对象。
__enter__(): 进入with代码块时调用,可以返回一个资源对象。__exit__(): 退出with代码块时调用,处理异常并进行清理工作。
示例:手写一个上下文管理器
import randomclass DummyContext:def __init__(self, name):self.name = namedef __enter__(self):"""进入上下文管理器,返回带随机后缀的 name"""return f'{self.name}-{random.random()}'def __exit__(self, exc_type, exc_val, exc_tb):"""退出上下文管理器时,执行清理操作"""print('Exiting DummyContext')with DummyContext('foo') as name:print(f'Name: {name}')
 
执行结果:
Name: foo-0.123456789
Exiting DummyContext
 
可以看到 __exit__ 方法无论有没有异常,都会在退出时执行。
3. with 语句的实际用途
 
(1)替代 try...finally 进行资源管理
 
在管理网络连接、数据库连接等资源时,通常会使用 try...finally 语句来确保即使发生异常,资源也会被正确释放。
 但 with 语句可以让代码更简洁。
传统 try...finally 方式:
conn = create_conn(host, port)
try:conn.send_text('Hello, world!')
except Exception as e:print(f'Unable to use connection: {e}')
finally:conn.close()  # 确保连接被关闭
 
使用 with 语句简化:
class create_conn_obj:"""创建连接对象,并在退出上下文时自动关闭"""def __init__(self, host, port):self.conn = create_conn(host, port)def __enter__(self):return self.conndef __exit__(self, exc_type, exc_value, traceback):self.conn.close()  # 退出时自动关闭return False  # 让异常继续传播(如果发生异常)with create_conn_obj(host, port) as conn:conn.send_text('Hello, world!')
 
在 with 代码块结束时,无论是否有异常,连接都会自动关闭。
(2)用于忽略特定异常
有时候,我们可能想忽略某些特定的异常,让代码继续执行。例如:
try:close_conn(conn)
except AlreadyClosedError:pass  # 忽略异常
 
这种写法虽然简单,但会导致 try...except 语句分散在代码各处,不易维护。
可以使用 上下文管理器 来封装忽略异常的逻辑:
class ignore_closed:"""忽略 AlreadyClosedError 异常"""def __enter__(self):passdef __exit__(self, exc_type, exc_value, traceback):if exc_type == AlreadyClosedError:return True  # 返回 True,表示异常已被处理,不再传播return False  # 其他异常继续传播with ignore_closed():close_conn(conn)  # 即使连接已关闭,也不会抛出错误
 
如果 close_conn(conn) 触发 AlreadyClosedError,它会被 ignore_closed 处理掉,而不会终止程序。
实际上,Python 标准库 contextlib 提供了 suppress() 方法,可以直接用:
from contextlib import suppresswith suppress(AlreadyClosedError):close_conn(conn)  # 忽略异常
 
这样代码更加简洁。
4. contextmanager 装饰器简化上下文管理器
 
如果只是为了实现简单的上下文管理器,不必写一个完整的类,可以使用 contextlib.contextmanager 装饰器,用函数+yield 来实现。
示例:
from contextlib import contextmanager@contextmanager
def create_conn_obj(host, port):"""创建连接对象,并在退出上下文时自动关闭"""conn = create_conn(host, port)try:yield conn  # 这里返回 conn,类似于 __enter__finally:conn.close()  # 退出时关闭连接,类似于 __exit__with create_conn_obj(host, port) as conn:conn.send_text('Hello, world!')
 
在 yield 之前的代码在进入 with 代码块时执行(类似 __enter__),
 yield 之后的代码在退出 with 代码块时执行(类似 __exit__)。
 相比手写类,这个方法更加简洁。
总结
with语句用于自动管理资源,替代try...finally,代码更简洁。- 上下文管理器(
__enter__和__exit__)可以实现with语句的功能,比如自动关闭文件、数据库连接、网络请求。 with可以用于异常处理,例如:- 替代 
try...finally进行资源清理 - 忽略特定异常
 
- 替代 
 @contextmanager装饰器可以简化上下文管理器,让代码更优雅。