with语句是Python中资源管理的最佳实践,它通过上下文管理器协议(__enter__和__exit__方法)确保资源的初始化与释放。使用with语句可自动处理文件、锁、数据库连接等资源的打开与关闭,无论代码块是否抛出异常,都能保证资源被正确清理,避免泄露。其核心优势在于提升代码的可读性、简洁性和异常安全性。相比传统的try...finally模式,with语句将资源管理逻辑封装在上下文管理器中,实现关注点分离,符合DRY原则。开发者可通过定义__enter__和__exit__方法来自定义上下文管理器,或利用contextlib模块中的@contextmanager装饰器、closing()、suppress()等工具快速创建上下文管理器,广泛应用于计时、临时目录切换、异常抑制等多种场景。掌握with语句及其生态是编写高质量Python代码的关键。

with
with
with
__enter__
with
__exit__
最经典的例子就是文件操作:
# 不使用 with 语句,需要手动关闭文件,容易忘记或处理异常不当
# file_obj = open('my_file.txt', 'w')
# try:
# file_obj.write('Hello, world!')
# finally:
# file_obj.close()
# 使用 with 语句,Python 会自动管理文件的开启和关闭
with open('my_file.txt', 'w', encoding='utf-8') as f:
f.write('你好,世界!\n')
f.write('这是 with 语句的魅力。\n')
# 文件在 with 块结束后自动关闭,即使中间发生错误
print("文件写入完成,文件已自动关闭。")在这个例子中,
open()
with open(...) as f:
立即学习“Python免费学习笔记(深入)”;
open()
__enter__
f
with
f
with
__exit__
__exit__
这不仅让代码更简洁,更重要的是,它提供了一种强大的异常安全保证。
with
在我看来,
with
try...finally
import threading
lock = threading.Lock()
# 传统方式,需要手动获取和释放锁
lock.acquire()
try:
print("线程安全操作进行中...")
# 可能会发生异常的代码
except Exception as e:
print(f"操作失败: {e}")
finally:
lock.release()
print("锁已释放。")这种
try...finally
finally
try...finally
with
__enter__
__exit__
with
更关键的是,
with
with
__exit__
try...finally
with
__enter__
__exit__
自定义上下文管理器,是理解
with
with
__enter__(self)
__exit__(self, exc_type, exc_val, exc_tb)
让我们来创建一个简单的自定义计时器上下文管理器,用于测量代码块的执行时间:
import time
class MyTimer:
def __enter__(self):
self.start_time = time.time()
print("计时开始...")
return self # 通常返回 self 或其他需要在 with 块中使用的对象
def __exit__(self, exc_type, exc_val, exc_tb):
end_time = time.time()
duration = end_time - self.start_time
print(f"计时结束。代码块执行了 {duration:.4f} 秒。")
# 处理异常:
# exc_type: 异常类型 (e.g., TypeError, ValueError)
# exc_val: 异常实例
# exc_tb: 异常的追踪信息 (traceback)
if exc_type is not None:
print(f"在计时块中捕获到异常:{exc_type.__name__}: {exc_val}")
# 如果 __exit__ 返回 True,则表示异常已被处理,不会再次向上抛出。
# 如果返回 False (或不返回任何值,默认为None),则异常会继续传播。
# 这里我们选择不抑制异常,让它继续传播,除非我们有特殊处理逻辑。
# return True # 如果想抑制异常,可以返回 True
print("清理工作完成。")
# 默认返回 None,意味着如果发生异常,异常会继续传播
# 如果返回 True,则表示异常已被处理,不会再次向上抛出。
# return False # 显式返回 False,与不返回相同效果现在,我们用它来计时:
with MyTimer():
print("正在执行一些耗时操作...")
time.sleep(0.5)
# 模拟一个可能发生的错误
# raise ValueError("哦豁,出错了!")
print("耗时操作完成。")
print("\n--- 带有异常的例子 ---")
try:
with MyTimer():
print("执行可能出错的操作...")
time.sleep(0.2)
result = 1 / 0 # 故意制造一个ZeroDivisionError
print(f"结果是: {result}")
except ZeroDivisionError:
print("外部捕获到了 ZeroDivisionError。")__enter__(self)
with
with ... as var:
var
with
self
None
MyTimer
self
with
MyTimer
None
as
__exit__(self, exc_type, exc_val, exc_tb)
with
exc_type
with
ZeroDivisionError
None
exc_val
ZeroDivisionError('division by zero')None
exc_tb
None
__exit__
True
with
__exit__
False
None
with
MyTimer
True
通过自定义上下文管理器,我们能够将任何一对“设置-清理”操作封装起来,使其能够与
with
with
contextlib
with
常见的应用场景:
线程/进程锁 (Locking): 在多线程或多进程编程中,为了避免竞态条件,我们经常需要获取锁并在操作完成后释放锁。
threading.Lock
multiprocessing.Lock
import threading
my_lock = threading.Lock()
shared_data = 0
def increment():
global shared_data
with my_lock: # 自动获取锁
# 这段代码是线程安全的
temp = shared_data
temp += 1
shared_data = temp
# 锁在 with 块结束后自动释放
print(f"线程 {threading.current_thread().name} 完成,shared_data: {shared_data}")
threads = [threading.Thread(target=increment, name=f"Thread-{i}") for i in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"最终 shared_data: {shared_data}")数据库连接 (Database Connections): 连接到数据库、执行操作、然后关闭连接是另一个典型场景。许多数据库库的连接对象都设计成了上下文管理器。
# 假设有一个简化的数据库连接类
class DatabaseConnection:
def __init__(self, db_name):
self.db_name = db_name
self.connection = None
def __enter__(self):
print(f"连接到数据库: {self.db_name}...")
# 模拟实际连接操作
self.connection = f"Connected to {self.db_name}"
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
if self.connection:
print(f"关闭数据库连接: {self.db_name}...")
# 模拟实际关闭操作
self.connection = None
if exc_type:
print(f"数据库操作发生异常: {exc_type.__name__}: {exc_val}")
with DatabaseConnection("my_app_db") as db:
print(f"正在使用连接: {db}")
# 执行数据库查询、更新等操作
# raise ValueError("模拟数据库操作失败")
print("数据库连接已处理。")网络套接字 (Network Sockets): 建立网络连接、发送/接收数据、关闭连接。
临时改变环境 (Temporary Context Change): 例如,临时改变当前工作目录,或临时修改某个配置。
contextlib
Python 标准库中的
contextlib
@contextmanager
contextlib
__enter__
__exit__
yield
__enter__
yield
__enter__
yield
__exit__
from contextlib import contextmanager
import time
@contextmanager
def simple_timer():
start_time = time.time()
print("(通过生成器)计时开始...")
try:
yield # 这里的 yield 相当于 __enter__ 的返回值,如果 with ... as var:,var 就得到 yield 的值
except Exception as e:
print(f"(通过生成器)在计时块中捕获到异常:{type(e).__name__}: {e}")
# 如果不重新抛出异常,异常会被抑制。
# raise # 如果想让异常继续传播,可以在这里重新抛出
finally:
end_time = time.time()
duration = end_time - start_time
print(f"(通过生成器)计时结束。代码块执行了 {duration:.4f} 秒。")
with simple_timer():
print("执行一些生成器计时操作...")
time.sleep(0.3)
# raise TypeError("模拟生成器计时错误")
print("生成器计时器已处理。")这种方式写起来非常简洁,对于那些逻辑相对简单的上下文管理场景,是首选。
closing(thing)
close()
close()
closing
from contextlib import closing
# 假设这是一个没有实现上下文管理协议的资源
class MyResource:
def __init__(self, name):
self.name = name
print(f"资源 '{self.name}' 已打开。")
def close(self):
print(f"资源 '{self.name}' 已关闭。")
def do_something(self):
print(f"资源 '{self.name}' 正在工作。")
with closing(MyResource("临时文件句柄")) as res:
res.do_something()
print("资源处理完成。")*`suppress(exceptions)`:** 这个上下文管理器用于临时抑制指定的异常。在某些情况下,你可能知道某个操作可能会抛出特定异常,但你希望程序继续执行,而不是中断。
from contextlib import suppress
with suppress(FileNotFoundError):
# 尝试打开一个不存在的文件,通常会抛出 FileNotFoundError
# 但在这里,这个异常会被 suppress 捕获并忽略
with open("non_existent_file.txt", "r") as f:
content = f.read()
print(content)
print("文件读取尝试完成 (如果文件存在)。")
print("程序继续执行,即使文件不存在。")
with suppress(ZeroDivisionError):
result = 10 / 0
print(f"这个不会打印,因为异常被抑制了: {result}")
print("除零错误被抑制,程序继续。")with
contextlib
以上就是Python怎么使用with语句_with语句与上下文管理器详解的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号