Prompts-MCP 686 skills · ai / design / design-pattern / framework / fundamentals / habit / lang / tech-selection /mcp/sse
lang lang/python/error-handling/eafp-vs-lbyl.md medium

py-err-eafp-vs-lbyl

EAFP(先做后捕获)vs LBYL(先检查后做)风格选择,及 contextlib.suppress 忽略预期异常。Use when 纠结用 try 还是 if 先检查 / 想消除 TOCTOU 竞态 / 静默忽略某个预期异常。

eafplbyl先检查后执行suppresscontextlib竞态条件
paths
  • *.py
  • py/**/*.py

Python · EAFP vs LBYL

规则

  • EAFP(Easier to Ask Forgiveness than Permission):直接做,失败用 try/except 兜。Python 的惯用风格。
  • LBYL(Look Before You Leap):先 if 检查再做。
用哪个 判据
EAFP 默认 正常路径成功率高;操作不可分(文件 / dict / 网络),检查与执行之间可能被改
LBYL 检查无副作用且廉价;失败是高频正常分支,靠异常代价高
contextlib.suppress(X) 只想忽略某个预期异常、不做任何处理

关键:LBYL 在并发 / 文件 / 外部状态下有 TOCTOU 竞态——检查通过到执行之间状态可能已变,EAFP 没有这个窗口。

正例

# EAFP:dict 取值,命中率高,一步到位
try:
    name = config["display_name"]
except KeyError:
    name = "匿名"
# 等价惯用写法:config.get("display_name", "匿名")

# EAFP:消除文件存在性的 TOCTOU 竞态
try:
    with open(path) as f:
        data = f.read()
except FileNotFoundError:
    data = default

contextlib.suppress 取代“空 except”,意图清晰:

import contextlib

with contextlib.suppress(FileNotFoundError):
    os.remove(tmp_path)        # 文件本就可能不存在,删不到无所谓

反例

# ❌ LBYL 检查文件 —— 检查到打开之间文件可能被删(TOCTOU)
if os.path.exists(path):
    with open(path) as f:    # 仍可能 FileNotFoundError
        ...

# ❌ 用 try/except/pass 静默忽略,意图不明、容易误吞别的异常
try:
    os.remove(tmp_path)
except Exception:            # 范围过宽,且 pass 像漏写
    pass
# ✅ 用 suppress 且只针对预期异常
with contextlib.suppress(FileNotFoundError):
    os.remove(tmp_path)

理由:os.path.exists 后再 open 存在竞态窗口,直接 open + 捕获更安全;suppress(具体类型)except Exception: pass 意图明确且不会误吞无关异常。注意 suppress 是“无声忽略”,需要日志/降级时仍用显式 except(见 no-bare-except)。

自检

  • [ ] 默认用 EAFP,只在检查廉价无副作用时才退回 LBYL?
  • [ ] 涉及文件 / 并发 / 外部状态时避免 LBYL 的 TOCTOU 竞态?
  • [ ] 只想忽略预期异常时用 contextlib.suppress(具体类型) 而非 except: pass
  • [ ] suppress 范围收窄到具体异常类型,没用 Exception 兜底?

相关