Python的with语句通过上下文管理器协议(__enter__和__exit__方法)实现资源的自动管理,确保其在使用后无论是否发生异常都能被正确释放。它简化了try...finally结构,广泛应用于文件操作、数据库事务、线程锁、临时状态更改和测试mock等场景,提升代码可读性与可靠性。

谈到Python的上下文管理器,尤其是
with
简单来说,Python的
with
__enter__
__exit__
with
with
__enter__
with
with
__exit__
__exit__
try...finally
对我来说,
with
with
try...finally
f = open('my_file.txt', 'w')
try:
f.write('Hello, world!')
except IOError as e:
print(f"写入错误: {e}")
finally:
f.close()这段代码本身没问题,但如果有很多这样的操作,代码里就会充斥着大量的
try...finally
close()
with
立即学习“Python免费学习笔记(深入)”;
with open('my_file.txt', 'w') as f:
f.write('Hello, world!')
# 文件在这里自动关闭,即使上面发生异常你看,代码是不是一下子清爽了很多?这种简化不仅仅是代码行数的减少,更重要的是,它降低了心智负担,让开发者可以更专注于业务逻辑的实现,而不是纠结于资源清理的细节。它提供了一个清晰的“入口”和“出口”,就像给资源管理画上了一个明确的边界,这种边界感,是提升代码可靠性的关键。
要自定义上下文管理器,通常有两种主要方式,各有各的适用场景。
第一种,也是最基础的,是实现一个类。 这个类需要定义
__enter__
__exit__
__enter__(self)
with
as
self
__exit__(self, exc_type, exc_val, exc_tb)
with
with
None
__exit__
True
False
举个例子,我们来创建一个模拟数据库连接的上下文管理器:
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"连接到 {self.db_name}"
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
print(f"数据库操作发生异常: {exc_val}")
# 可以在这里执行回滚操作
print("正在回滚事务...")
print(f"正在关闭数据库 '{self.db_name}'...")
# 模拟关闭连接
self.connection = None
# 如果我们不想让异常继续传播,可以返回True
# return True 使用它:
with DatabaseConnection('my_app_db') as db:
print(f"成功获取连接: {db}")
# 模拟一些数据库操作
# raise ValueError("模拟一个数据库操作错误")
print("with语句块已结束。")第二种,更Pythonic、更简洁的方式,是使用contextlib.contextmanager
from contextlib import contextmanager
@contextmanager
def managed_resource(name):
print(f"资源 '{name}' 正在被获取...")
try:
yield f"处理资源 {name}" # yield之前是__enter__,yield之后是__exit__
finally:
print(f"资源 '{name}' 正在被释放...")
with managed_resource('日志文件') as res:
print(f"正在使用: {res}")
# raise TypeError("模拟一个使用错误")这里,
yield
yield
__enter__
yield
with
__exit__
@contextmanager
上下文管理器不仅仅是文件操作那么简单,它在很多高级和复杂的场景下都展现出强大的威力。
1. 数据库事务管理: 这是最典型的应用之一。在进行一系列数据库操作时,我们希望这些操作要么全部成功并提交,要么全部失败并回滚。
with
# 伪代码,实际会与具体的ORM或DB API结合
class Transaction:
def __init__(self, connection):
self.connection = connection
def __enter__(self):
self.connection.begin()
return self.connection
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
self.connection.rollback()
print("事务回滚。")
else:
self.connection.commit()
print("事务提交。")
# 使用
# with Transaction(my_db_conn) as conn:
# conn.execute("INSERT ...")
# conn.execute("UPDATE ...")
# # 如果这里发生异常,事务会自动回滚这样,即使中间某个操作失败,数据库也能保持一致性,这在业务逻辑中至关重要。
2. 线程/进程锁管理: 在多线程或多进程编程中,为了避免数据竞争,我们经常需要使用锁(
threading.Lock
multiprocessing.Lock
acquire()
release()
release()
with
import threading
lock = threading.Lock()
def critical_section():
with lock: # 自动acquire,with块结束自动release
print(f"{threading.current_thread().name} 正在访问共享资源...")
# 模拟一些操作
import time
time.sleep(0.1)
print(f"{threading.current_thread().name} 释放了锁。")
# 启动多个线程
# threads = [threading.Thread(target=critical_section, name=f"Thread-{i}") for i in range(5)]
# for t in threads:
# t.start()
# for t in threads:
# t.join()这确保了锁总是在关键代码块执行完毕后被释放,极大地提升了并发编程的安全性。
3. 临时改变系统状态: 有时候,我们可能需要在代码执行期间临时改变一些全局或系统级别的设置,例如改变当前工作目录、修改环境变量、或者重定向标准输出/输入。
contextlib
import os
from contextlib import chdir, redirect_stdout
# 临时改变工作目录
print(f"当前目录: {os.getcwd()}")
with chdir('/tmp'):
print(f"进入 /tmp 后的目录: {os.getcwd()}")
# 在这里执行一些与/tmp相关的操作
print(f"with块结束后目录: {os.getcwd()}")
# 临时重定向标准输出到文件
with open('output.log', 'w') as f:
with redirect_stdout(f):
print("这条信息会被写入 output.log")
print("这条信息会打印到控制台")这种模式使得局部状态的改变变得非常可控,避免了对全局状态的长期污染。
4. 测试中的Mocking: 在单元测试中,我们经常需要模拟(mock)外部依赖,比如数据库调用、API请求等。
unittest.mock.patch
with
from unittest.mock import patch
def fetch_data_from_api():
# 假设这里会真的调用一个外部API
return "真实API数据"
def process_data():
data = fetch_data_from_api()
return f"处理后的数据: {data}"
# 在测试中
with patch('__main__.fetch_data_from_api', return_value="模拟API数据"):
result = process_data()
print(f"测试结果: {result}") # 输出:处理后的数据: 模拟API数据
# with块结束后,fetch_data_from_api恢复为原始实现
print(f"with块结束后真实数据: {process_data()}") # 输出:处理后的数据: 真实API数据这让测试变得更加隔离和可重复,无需实际的外部依赖。
这些例子只是冰山一角,上下文管理器的核心理念——“封装资源的获取与释放,并保证其在任何情况下都能正确执行”——使其成为Python中一个非常强大且灵活的工具,值得我们深入理解和应用。
以上就是谈谈你对Python上下文管理器的理解(with语句)。的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号