从 if err != nil 到 try/except
异常是 Python 的主流错误通道。panic/recover 对应 raise/except 顶层兜底,%w 包装对应 raise from。
12 分钟 · 右栏代码可点击「运行」
同一个错误传播
Go
1func load(path string) ([]byte, error) {2 data, err := os.ReadFile(path)3 if err != nil {4 return nil, fmt.Errorf(5 "load %q: %w", path, err)6 }7 return data, nil8}910// 调用处11data, err := load("cfg.toml")12if err != nil {13 log.Fatal(err)14}
Python
1def load(path: str) -> bytes:2 try:3 return open(path, "rb").read()4 except OSError as e:5 raise RuntimeError(6 f"load {path!r}: {e}") from e78# 调用处9try:10 data = load("cfg.toml")11except RuntimeError as e:12 print("fatal:", e)
fmt.Errorf(..., %w)→raise X(...) from e,异常链都会保留原因;errors.Is/As→ 多层except SpecificError。- 函数签名不再返回 error —— 失败通道挪到了异常,返回值就是真返回值。
自定义错误 → 自定义异常
Go
1type ValidationError struct {2 Field string3}45func (e *ValidationError) Error() string {6 return "bad field: " + e.Field7}89if bad {10 return &ValidationError{Field: "age"}11}
Python
1class ValidationError(Exception):2 def __init__(self, field: str):3 super().__init__(f"bad field: {field}")4 self.field = field56if bad:7 raise ValidationError("age")
注意
不要把 Go 的习惯带进来 — 在 Python 里写 def load(path) -> tuple[bytes, str | None] 这种 (result, err) 返回值会被认为是坏味道。错误走异常,正常值走返回值,二者不混。panic/recover 在 Python 里的对应物是「顶层 except 兜底记录日志」,而不是常规控制流。
TIP
EAFP 会在你身上发生 — 写惯 if ok, err := ... 之后,Python 的 try: return cache[k] / except KeyError: 会先让你不安,然后让你上瘾 —— 检查与使用之间的竞态窗口消失了。
练一练
这张卡的示例代码已在游乐场中备好,改一改再运行。