Python 第九节 with关键字使用详细介绍 案例分析及注意事项
·
前言
with 关键字是 Python 中用于上下文管理的重要语法结构,它能够自动管理资源的获取和释放,确保即使在发生异常的情况下也能正确清理资源。
一、with基本语法
with context_expression [as target]:
# with 代码块
statements
二、工作原理
with 语句背后的机制是上下文管理器协议,任何实现了 __enter__() 和 __exit__() 方法的对象都可以作为上下文管理器。
执行流程:
- 执行
context_expression,获取上下文管理器 - 调用上下文管理器的
__enter__()方法 - 如果有 as 子句,将
__enter__()的返回值赋给target - 执行
with代码块 - 无论代码块是否发生异常,都调用
__exit__()方法
三、常见使用场景
文件操作(最常用)
# 传统方式需要手动关闭文件
file = open('example.txt', 'r')
try:
content = file.read()
finally:
file.close()
# 使用 with 语句,自动关闭文件
with open('example.txt', 'r') as file:
content = file.read()
# 文件会自动关闭,无需手动调用 file.close()
数据库连接
import sqlite3
# 自动管理数据库连接
with sqlite3.connect('database.db') as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM users')
results = cursor.fetchall()
# 连接会自动关闭
线程锁
import threading
lock = threading.Lock()
# 自动获取和释放锁
with lock:
# 临界区代码
print("线程安全操作")
# 锁会自动释放
临时目录/文件
import tempfile
# 自动创建和清理临时目录
with tempfile.TemporaryDirectory() as temp_dir:
print(f"临时目录: {temp_dir}")
# 在临时目录中工作
# 临时目录会自动删除
四、自定义上下文管理器
基于类的实现
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):
print(f"关闭数据库连接: {self.db_name}")
self.connection = None
# 如果返回 True,则异常不会被传播
return False
# 使用自定义上下文管理器
with DatabaseConnection('my_db') as db:
print(f"使用数据库: {db}")
# 数据库操作
使用 contextlib 模块
from contextlib import contextmanager
@contextmanager
def timer():
import time
start = time.time()
try:
yield
finally:
end = time.time()
print(f"执行时间: {end - start:.2f}秒")
# 使用上下文管理器装饰器
with timer():
# 需要计时的代码
time.sleep(2)
更复杂的例子
class FileHandler:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
self.line_count = 0
def __enter__(self):
self.file = open(self.filename, self.mode)
print(f"打开文件: {self.filename}")
return self
def read_line(self):
line = self.file.readline()
if line:
self.line_count += 1
return line
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
print(f"关闭文件: {self.filename}")
print(f"总共读取了 {self.line_count} 行")
# 处理异常
if exc_type is not None:
print(f"发生异常: {exc_type}: {exc_val}")
return False # 不抑制异常
# 使用自定义文件处理器
with FileHandler('example.txt', 'r') as fh:
while True:
line = fh.read_line()
if not line:
break
print(line.strip())
五、多个上下文管理器
可以同时使用多个上下文管理器:
# 同时打开多个文件
with open('file1.txt', 'r') as f1, open('file2.txt', 'r') as f2:
content1 = f1.read()
content2 = f2.read()
# 两个文件都会自动关闭
# 或者写成多行形式
with open('file1.txt', 'r') as f1, \
open('file2.txt', 'r') as f2, \
open('output.txt', 'w') as f3:
# 处理多个文件
pass
六、高级用法
异常处理在 exit 中
class ErrorHandler:
def __enter__(self):
print("进入上下文")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
print(f"捕获到异常: {exc_type.__name__}: {exc_val}")
# 返回 True 可以抑制异常
return True
print("正常退出上下文")
return False
with ErrorHandler():
print("正常操作")
with ErrorHandler():
raise ValueError("这是一个错误") # 异常会被捕获但不会传播
异步上下文管理器(Python 3.5+)
import asyncio
class AsyncDatabaseConnection:
async def __aenter__(self):
print("异步连接数据库")
await asyncio.sleep(1) # 模拟异步连接
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("异步关闭数据库连接")
await asyncio.sleep(0.5) # 模拟异步关闭
async def main():
async with AsyncDatabaseConnection() as db:
print("使用异步数据库连接")
# asyncio.run(main())
七、注意事项
异常处理
__exit__方法会接收异常信息- 如果
__exit__返回True,异常会被抑制,不会传播 - 通常应该返回
False让异常正常传播
资源泄漏
虽然 with 语句能自动管理资源,但如果在 __enter__ 中获取资源失败,__exit__ 可能不会被调用:
class RiskyResource:
def __enter__(self):
print("获取资源")
if some_condition: # 如果这里失败
raise Exception("获取资源失败")
return self
def __exit__(self, *args):
print("释放资源") # 如果 __enter__ 失败,这行不会执行
性能考虑
with 语句会有轻微的性能开销
在性能敏感的循环中,应考虑在循环外部使用 with
嵌套使用
# 嵌套的 with 语句
with context1() as c1:
with context2() as c2:
# 操作 c1 和 c2
pass
八、实际应用案例
事务管理
class Transaction:
def __init__(self, db):
self.db = db
self.committed = False
def __enter__(self):
print("开始事务")
return self
def commit(self):
self.committed = True
print("提交事务")
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None or not self.committed:
print("回滚事务")
else:
print("事务完成")
# 使用事务
db = "模拟数据库"
with Transaction(db) as tx:
# 数据库操作
if operation_successful:
tx.commit()
性能分析
import time
from contextlib import contextmanager
@contextmanager
def profile(operation_name):
start = time.perf_counter()
try:
yield
finally:
end = time.perf_counter()
print(f"{operation_name} 耗时: {end - start:.4f}秒")
# 分析代码性能
with profile("数据处理"):
# 需要分析的代码
time.sleep(0.1)
总结
with 语句是 Python 中非常重要的资源管理工具,主要有一下特点:
- 使代码更简洁、可读
- 确保资源正确释放,避免泄漏
- 提供统一的异常处理机制
- 支持自定义上下文管理器
更多推荐
所有评论(0)