with 语句的作用
with 是 Python 中处理资源(如文件、网络连接、数据库连接)的一种 语法糖,目的是为了确保使用完资源后,能自动清理资源,比如关闭文件、释放锁、断开连接等。
不使用 with
的资源处理方式(容易忘记关闭):
f = open('example.txt', 'r')
try:
content = f.read()
finally:
f.close() # 必须手动关闭
使用 with
的方式(自动关闭):
with open('example.txt', 'r') as f:
content = f.read()
# 离开 with 代码块后,f 会自动关闭
上下文管理器(Context Manager)
上下文管理器是一个实现了 __enter__()
和 __exit__()
方法的对象。
这两个方法控制了:
__enter__()
:进入with
块时调用,返回值绑定给as
后面的变量。__exit__()
:退出with
块时调用,无论是否抛出异常,都会执行,通常用于清理操作。
with
语句的执行流程
with expression as variable:
# do something
等价于如下代码:
mgr = expression # mgr 是一个上下文管理器对象
var = mgr.__enter__() # 调用 __enter__ 方法
try:
# do something
finally:
mgr.__exit__(exc_type, exc_value, traceback) # 调用 __exit__ 方法
如果代码块中发生异常,异常信息会传给 __exit__
的三个参数:
exc_type
:异常类型(如ValueError
)exc_value
:异常实例对象traceback
:异常的 traceback 信息
模拟一个文件打开的上下文管理器
我们来自己写一个类,模拟 Python 内置的 open()
行为:
class FileContextManager:
def __init__(self, file_name, mode='r'):
self.file_name = file_name # 文件名
self.mode = mode # 读写模式
self.file = None # 文件对象,初始为 None
def __enter__(self):
print(f"打开文件: {self.file_name},模式: {self.mode}")
# 打开文件并返回文件对象
self.file = open(self.file_name, self.mode)
return self.file # 返回文件对象
def __exit__(self, exc_type, exc_value, traceback):
if self.file is not None:
print("关闭文件")
self.file.close() # 确保文件被关闭
if exc_type: # 检查是否有异常
print(f"发生异常: {exc_value}")
return True # 处理异常并防止抛出
return False # 允许异常向上抛出
使用方式:
# 使用自定义文件上下文管理器
try:
with FileContextManager('example.txt', 'w') as file: # 打开文件进行写入
print("在上下文中写入一些内容")
file.write("Hello, world!\n")
file.write("这是第二行。\n")
# 再读取之前写入的数据
with FileContextManager('example.txt', 'r') as file: # 打开文件进行读取
print("读取文件内容:")
content = file.read()
print(content) # 输出文件内容
except Exception as e:
print(f"处理外部异常: {e}")

contextlib 模块简化上下文管理器
@contextmanager
装饰器
from contextlib import contextmanager
@contextmanager
def my_context():
print("进入上下文") # 相当于 __enter__
try:
yield "资源" # yield 让出控制权,运行 with 块的代码
except Exception as e:
print(f"处理中发生异常: {e}") # 你可以处理异常,也可以选择不写
raise # 重新抛出(可选)
finally:
print("退出上下文") # 相当于 __exit__,无论是否异常都会执行
# 使用:
with my_context() as res:
print("处理中...", res)
# raise ValueError("发生错误") # 可加这一句测试异常处理
yield
前:等于__enter__()
,通常是资源申请、初始化的地方。yield
后:等于__exit__()
,是资源清理、异常处理的地方。- 把 try-finally 包住
yield
,是为了模仿底层的上下文管理器协议。
closing()
示例(对没有实现 __enter__
/ __exit__
的对象进行封装)
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('http://example.com')) as page:
for line in page:
print(line)
Python 内置库 urllib
中的函数,用于打开一个 URL(网页),并返回一个类文件对象(类比 open()
打开的文件对象),它返回的对象具有 .read()
、.close()
等方法,你可以像读文件一样读网页。因为 urlopen()
返回的对象没有实现 __enter__
/ __exit__
方法,所以不能直接用在 with
语句中(with urlopen('http://example.com') as page: # ❌ 会报错!)
closing()
是什么?
这是 contextlib
提供的一个工具函数,它的作用是:将“拥有 close() 方法但不是上下文管理器”的对象包装成一个可以用在 with
中的对象。
它的底层实现类似于这样:
class closing:
def __init__(self, thing):
self.thing = thing
def __enter__(self):
return self.thing
def __exit__(self, exc_type, exc_val, exc_tb):
self.thing.close() # 离开 with 时自动调用 close()
也就是说:
with closing(urlopen(...)) as page:
就相当于:
page = urlopen(...)
try:
...
finally:
page.close() # 自动关闭连接,释放资源
常见的上下文管理器应用场景
场景 | 说明 |
---|---|
文件读写 | with open('file.txt') as f: 自动关闭文件 |
锁(多线程) | with lock: 自动释放锁 |
数据库连接 | with connection.cursor() as cursor: 自动提交或回滚 |
临时修改状态 | 比如临时更改目录,环境变量等 |
发布者:LJH,转发请注明出处:https://www.ljh.cool/43109.html