一、上下文管理器
with 语句依赖于 上下文管理器(Context Manager),这是一个实现了 __enter__ 和 __exit__ 方法的对象。__enter__ 方法在进入 with 代码块时调用,通常用于获取资源;__exit__ 方法在退出 with 代码块时调用,通常用于释放资源。
二、with 语句的基本语法
with context_manager as variable:
# 执行代码块
其中,context_manager 是一个实现了上下文管理协议的对象,variable 是可选的,用于接收 enter 方法返回的值。
三、with 语句的常见用法
1 文件操作
通过 with 语句打开文件,可以在文件使用完毕后自动关闭,无需显式调用 close() 方法。
示例:读取文件内容
with open(‘example.txt’, ‘r’) as file:
content = file.read()
print(content)
示例:写入文件内容
with open('output.txt', 'w') as file:
file.write("Hello, World!")
2. 网络连接
在网络编程中,with 语句可以用于管理网络连接,确保连接在使用完毕后自动关闭。例如,使用 requests 库发送 HTTP 请求时,可以通过 with 语句管理会话(Session)对象。
示例:使用 requests 发送 HTTP 请求
import requests
with requests.Session() as session:
response = session.get('https://api.example.com/data')
print(response.json())
在这个例子中,Session 对象会在 with 代码块结束时自动关闭,无需显示调用close()方法,确保资源被正确释放。
3. 数据库连接
在数据库操作中,with 语句可以用于管理数据库连接,确保连接在使用完毕后自动关闭。例如,使用 sqlite3 库连接 SQLite 数据库时,可以通过 with 语句管理连接对象。
示例:使用 sqlite3 连接数据库
import sqlite3
with sqlite3.connect('example.db') as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
rows = cursor.fetchall()
for row in rows:
print(row)
在这个例子中,connect() 函数返回一个数据库连接对象,该对象实现了上下文管理协议。with 语句确保在代码块结束时自动调用 conn.close(),即使在执行 SQL 查询时发生异常,连接也会被正确关闭。
4. 锁机制
在多线程编程中,with 语句可以用于管理锁(Lock),确保锁在使用完毕后自动释放。例如,使用 threading.Lock 时,可以通过 with 语句管理锁对象。
示例:使用 threading.Lock 实现线程同步
import threading
lock = threading.Lock()
def thread_function():
with lock:
print(f"Thread {threading.current_thread().name} is running")
threads = []
for i in range(5):
t = threading.Thread(target=thread_function, name=f"Thread-{i+1}")
threads.append(t)
t.start()
for t in threads:
t.join()
在这个例子中,lock 对象会在 with 代码块结束时自动释放,确保多个线程不会同时访问共享资源,从而避免竞态条件。
5. 自定义上下文管理器
除了内置的上下文管理器,你还可以通过实现 __enter__ 和 __exit__ 方法来自定义上下文管理器。这使得 with 语句可以用于更广泛的应用场景。
示例:自定义上下文管理器
假设我们想创建一个上下文管理器来记录某个代码块的执行时间。我们可以定义一个类 Timer,并在其中实现 __enter__ 和 __exit__ 方法。
import time
class Timer:
def __enter__(self):
self.start_time = time.time()
return self
def __exit__(self, exc_type, exc_value, traceback):
end_time = time.time()
elapsed_time = end_time - self.start_time
print(f"Elapsed time: {elapsed_time:.2f} seconds")
# 使用自定义上下文管理器
with Timer():
time.sleep(2)
在这个例子中,Timer 类实现了上下文管理协议。__enter__ 方法记录开始时间,__exit__ 方法计算并打印经过的时间。with 语句确保在代码块结束时自动调用 __exit__ 方法,从而实现对代码块执行时间的精确测量。
6. 多个上下文管理器
with 语句支持同时管理多个上下文管理器,只需将它们用逗号分隔即可。这对于需要同时管理多个资源的场景非常有用。
示例:同时管理多个文件
假设我们需要同时读取两个文件的内容并进行比较。我们可以使用 with 语句同时管理两个文件对象。
with open('file1.txt', 'r') as f1, open('file2.txt', 'r') as f2:
content1 = f1.read()
content2 = f2.read()
if content1 == content2:
print("Files are identical")
else:
print("Files are different")
在这个例子中,with 语句同时管理两个文件对象 f1 和 f2,确保它们在代码块结束时自动关闭。
7. 异常处理
with 语句不仅可以管理资源,还可以捕获和处理异常。__exit__ 方法可以接受三个参数:exc_type、exc_value 和 traceback,分别表示异常类型、异常值和堆栈跟踪。如果 __exit__ 方法返回 True,则表示异常已被处理,不会传播到外部;如果返回 False 或不返回任何值,则异常会继续传播。
示例:捕获异常
class ClsContext:
def __enter__(self):
print("上下文管理器 __enter__()")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"上下文管理器 __exit__() exc_type={exc_type};exc_val={exc_val};exc_tb={exc_tb}")
return True
def test(self):
print("上下文管理器 test() 1")
try:
json.loads('{"a":')
except json.JSONDecodeError as e:
print("上下文管理器 test() json.loads error")
raise json.JSONDecodeError() from e
print("上下文管理器 test() 2")
with ClsContext() as cc:
print("with语句中的代码块 1")
cc.test()
print("with语句中的代码块 2")
输出:
上下文管理器 __enter__()
with语句中的代码块 1
上下文管理器 test() 1
上下文管理器 test() json.loads error
上下文管理器 __exit__() exc_type=<class 'TypeError'>;exc_val=JSONDecodeError.__init__() missing 3 required positional arguments: 'msg', 'doc', and 'pos';exc_tb=<traceback object at 0x000001F0659014C0>
4. 参考资料
https://blog.csdn.net/zhaoxilengfeng/article/details/144382104

103

被折叠的 条评论
为什么被折叠?



