With
with 用于自动处理 资源申请 -> 使用 -> 释放 的流程。
with resource as r:
r.do()
好处:
- 自动关闭文件、连接、游标等
- 避免泄漏
- 避免 finally
- 代码更简洁、可读性更高
with open("data.txt", "r") as f:
content = f.read()
离开缩进块后:
- 自动调用 f.close()
要让一个对象能被 with 使用,它必须实现:
__enter__(self)
__exit__(self, exc_type, exc, tb)
流程:
with obj as x:
# 进入前会执行:x = obj.__enter__()
# 离开时一定执行:obj.__exit__()
class MyTimer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc, tb):
import time
print("耗时:", time.time() - self.start)
# 返回 False 表示异常继续往外抛
return False
with MyTimer():
print("doing something...")
输出:
doing something...
耗时: 0.00123
适合快速写 with:
from contextlib import contextmanager
@contextmanager
def open_db():
conn = create_connection()
try:
yield conn
finally:
conn.close()
使用:
with open_db() as conn:
conn.query(...)
with open("a.txt") as f:
...
with SessionLocal() as session:
session.add(...)
session.commit()
from threading import Lock
lock = Lock()
with lock:
# 自动 acquire / release
with requests.get(url, stream=True) as resp:
...
from tempfile import TemporaryDirectory
with TemporaryDirectory() as tmpdir:
...
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove("test.txt")
__exit__ 接收三个参数:
| 参数 | 说明 |
|---|---|
| exc_type | 异常类型 |
| exc | 异常对象 |
| tb | traceback |
如果 __exit__ 返回 True:
- 异常被认为“已处理”,不会向上抛出
例如:
class IgnoreError:
def __exit__(self, exc_type, exc, tb):
return True # 丢弃异常
with open("a.txt") as f1, open("b.txt") as f2:
...
你可以选择:
- 不处理(返回 False)
- 吃掉异常(返回 True)
- 自己抛出新异常
避免手动 close
如你常用的 Database session
原则:with 块越小越好
比如锁、临时文件、网络流
| 功能 | 写法 |
|---|---|
| 文件操作 | with open(...) as f: |
| 自定义类 | 定义 __enter__ + __exit__ |
| 快速自定义 | @contextmanager |
| 忽略异常 | with suppress(Exception): |
| 管理锁 | with lock: |
| 数据库会话 | with Session() as s: |
| 多上下文 | with A() as a, B() as b: |
| 退出清理 | 写在 __exit__() 或 finally |