是什么
这是 Python 中非常重要的语法糖,核心作用是简化资源管理,让代码更简洁、更安全。
核心概念:上下文管理器
with 关键字的底层是上下文管理器(Context Manager) ------ 这是一种实现了 __enter__() 和 __exit__() 两个魔法方法的对象。
with语句执行时,会先调用__enter__()方法,返回的结果可以赋值给as后的变量;- 当
with代码块执行完毕(无论正常结束还是抛出异常),都会自动调用__exit__()方法,完成资源的清理(比如关闭文件、释放锁、断开连接)。
简单来说:with 帮你自动做"收尾工作" ,不用手动写 close()、release() 这类代码,避免因忘记释放资源导致内存泄漏、文件句柄耗尽等问题。
最常见的使用场景:文件操作
先看对比,就能直观感受到 with 的优势:
不用 with 的写法(容易出错)
python
# 手动管理文件资源,容易忘记 close()
f = open("test.txt", "w")
try:
f.write("Hello Python")
finally:
# 必须手动关闭,否则文件句柄不会释放
f.close()
用 with 的写法(简洁安全)
python
# with 自动调用 close(),无论代码块是否出错
with open("test.txt", "w") as f:
f.write("Hello Python")
# 代码块结束后,文件已自动关闭,无需手动操作
其他常见场景
除了文件操作,with 还广泛用于:
- 线程锁:自动获取/释放锁,避免死锁
python
import threading
lock = threading.Lock()
# 自动 acquire() 锁,代码块结束后自动 release()
with lock:
# 临界区代码,确保线程安全
print("线程安全的操作")
- 数据库连接:自动断开连接
python
import sqlite3
# 自动关闭数据库连接
with sqlite3.connect("test.db") as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
# 连接已自动关闭
自定义上下文管理器(进阶)
如果你想自己实现一个支持 with 的对象,只需定义 __enter__ 和 __exit__ 方法:
python
class MyContext:
# 进入 with 代码块时执行
def __enter__(self):
print("进入上下文:准备资源")
# 返回的值会赋值给 as 后的变量
return "我是上下文返回的内容"
# 退出 with 代码块时执行
# exc_type: 异常类型,exc_val: 异常值,exc_tb: 异常栈(无异常则为 None)
def __exit__(self, exc_type, exc_val, exc_tb):
print("退出上下文:清理资源")
# 如果返回 True,会抑制异常(不向外抛出);返回 False 则正常抛出
return False
# 使用自定义上下文管理器
with MyContext() as ctx:
print(f"执行代码块,获取到的值:{ctx}")
# 模拟异常(取消注释看效果)
# raise ValueError("测试异常")
# 输出:
# 进入上下文:准备资源
# 执行代码块,获取到的值:我是上下文返回的内容
# 退出上下文:清理资源
简化自定义上下文:contextlib 装饰器
对于简单的场景,不用写类,用 contextlib.contextmanager 装饰器可以快速实现上下文管理器(基于生成器):
python
from contextlib import contextmanager
@contextmanager
def my_simple_context():
# __enter__ 部分:生成器 yield 之前的代码
print("进入简单上下文")
yield "简单上下文的返回值" # yield 的值赋值给 as 后的变量
# __exit__ 部分:生成器 yield 之后的代码
print("退出简单上下文")
# 使用
with my_simple_context() as ctx:
print(f"代码块执行,值:{ctx}")
总结
with是 Python 提供的资源管理语法糖,核心是上下文管理器,自动完成"资源申请-使用-释放"的流程;- 核心优势:简化代码 + 保证安全,避免忘记释放资源或异常导致的资源泄漏;
- 常用场景:文件操作、锁管理、数据库连接等需要手动释放资源的场景,也可自定义上下文管理器适配业务需求。
简单记:只要你需要"先打开/获取,后关闭/释放"的操作,优先用 with,既省心又不容易出错。