lang
lang/python/context-managers/exitstack-suppress.md
medium
py-ctx-exitstack-suppress
contextlib 工具 ExitStack 动态多资源、suppress 忽略异常、nullcontext 占位、redirect_stdout。Use when 资源数量运行时才定 / 想跳过 try-except-pass / 条件性进 with / 重定向输出。
动态资源exitstacksuppressnullcontextredirect_stdout
paths
*.pypy/**/*.py
Python · ExitStack / suppress / nullcontext
规则
| 工具 | 用途 | 何时用 |
|---|---|---|
ExitStack() |
把任意个上下文管理器压入同一个栈,退出时逆序全部关闭 | 资源个数运行时才知道(如打开一批文件) |
stack.enter_context(cm) |
进入一个 cm 并登记,返回其 __enter__ 值 |
在循环里逐个进栈 |
stack.callback(fn, *a) |
登记一个普通清理回调(非上下文管理器) | 清理无 with 接口的资源 |
suppress(*excs) |
忽略指定异常,等价于 try/except <excs>: pass |
删不存在的文件等"没有就算了"场景 |
nullcontext(x) |
什么都不做的占位 cm,as 得到 x |
条件性决定是否真正进 with |
redirect_stdout(f) |
临时把 print 重定向到 f |
捕获第三方库的 stdout |
正例
from contextlib import ExitStack, suppress, nullcontext, redirect_stdout
import io
# 1. 动态多资源:N 个文件全部安全关闭,逆序退出
def merge(paths: list[str], out: str) -> None:
with ExitStack() as stack:
files = [stack.enter_context(open(p)) for p in paths]
with open(out, "w") as dst:
for f in files:
dst.write(f.read()) # 任一步抛异常,已开的文件都会被关
# 2. suppress:替代 try/except-pass
with suppress(FileNotFoundError):
os.remove("cache.tmp") # 不存在就算了,不报错
# 3. nullcontext:条件性进 with,避免 if/else 重复块
cm = open(path) if path else nullcontext(sys.stdin)
with cm as src:
process(src)
# 4. redirect_stdout:捕获库的 print
buf = io.StringIO()
with redirect_stdout(buf):
noisy_lib.run()
captured = buf.getvalue()
反例
# ❌ 1. 嵌套 with 管理可变数量资源 —— 写死层数,数量一变就崩
with open(a) as fa, open(b) as fc: # 只能处理固定个数
...
# ❌ 2. try/except 后 pass —— 用 suppress 更短且意图明确
try:
os.remove(p)
except FileNotFoundError:
pass # → with suppress(FileNotFoundError):
# ❌ 3. suppress(Exception) 一把抓 —— 退化成裸 except,吞掉真错误
with suppress(Exception): # 只 suppress 你预期的具体异常
risky()
# ❌ 4. 用 if/else 重复整段逻辑,只为决定要不要进 with —— 改 nullcontext
if path:
with open(path) as f: process(f)
else:
process(sys.stdin)
自检
- [ ] 资源个数运行时才定 → 用
ExitStack,没有写死嵌套with? - [ ]
try/except <Specific>: pass已替换为suppress(<Specific>)? - [ ]
suppress传的是具体异常,不是Exception? - [ ] 条件性进
with用nullcontext占位,没有重复代码块?