第 09 课 · 入门 · 19 分钟

Python 错误处理

捕获可预期的失败,给出有用的兜底结果,让程序继续运行。

try/exceptValueError 与 KeyErrorfinally

客人在数量栏里打了「三」,收银台就在队伍中间卡住了。这个失败完全可以预料,恢复也该如此。哪些异常值得接住,哪些应该让程序干脆停下来?

错误是一种信息。有些是应该修复的 bug;有些是预期内的失败,例如用户输入了非法数字,或者记录缺少字段。try 和 except 可以处理预期内的错误,同时不掩盖其他问题。 你还会在循环里跳过一个坏值,并带上下文重新抛出异常。

捕获具体异常

把可能出错的代码放进 try。只捕获你预期并且能够处理的异常,例如 int() 失败产生的 ValueError。不要使用裸 except,因为它会连你需要看到的编程错误一起隐藏。

text = "12"
try:
    value = int(text)
except ValueError:
    value = 0
print(value)

安全的类型转换

不同错误,不同处理

字典缺少键会触发 KeyError,文本无法转成数字会触发 ValueError。捕获错误的类型不对,处理代码不会执行。只捕获你真正能恢复的异常。

order = {}
try:
    price = order["price"]
except KeyError:
    price = 0
print(price)

缺少字段

finally 与 else

try 没有发生异常时,else 会执行。无论成功还是失败,finally 都会执行,适合做清理工作,例如关闭资源。

try:
    print("处理中")
except ValueError:
    print("失败")
else:
    print("成功")
finally:
    print("结束")

完整的 try 结构

只捕获你能处理的异常

只捕获你能处理的异常。裸 except 连拼写错误和程序错误一起吞掉,调试会更难。在循环里,为单个坏值捕获 ValueError,然后继续处理其余值。

raw_values = ["3", "many", "5"]
numbers = []
for raw in raw_values:
    try:
        numbers.append(int(raw))
    except ValueError:
        print("skipping:", raw)
print(numbers)

从一行坏数据中恢复

重新抛出前补充上下文

重新抛出时补充上下文,但不要丢掉原始原因;raise ... from error 会保留异常链。用 repr() 显示坏值,空格和换行才看得见。

def parse_price(raw):
    try:
        return float(raw)
    except ValueError as error:
        raise ValueError(f"invalid price: {raw!r}") from error

for raw in ["4.50", "free"]:
    try:
        print(parse_price(raw))
    except ValueError as error:
        print(error)

保留原因,补上坏输入

只捕获你能处理的错误

裸 except 会捕获所有异常,包括你修不了的拼写错误和中断。只捕获你预期的异常,例如 int() 抛出的 ValueError。else 放成功后才执行的代码,finally 放无论如何都要做的清理。如果你说不清拿到这个错误之后要做什么,就让它继续向上抛。

raw = "three"
try:
    qty = int(raw)
except ValueError:
    print("不是数字")
else:
    print("数量", qty)
finally:
    print("结束")

具体处理,明确清理

本课要点

  • 只捕获你能够处理的具体异常。
  • ValueError 对应转换失败,KeyError 对应字典缺键。
  • else 在 try 成功后执行,finally 总是执行。
  • 不要用裸 except 静默吞掉所有 bug。
  • 捕获最窄的异常,重抛时补充上下文。
  • 只捕获你能处理的异常,让意外错误暴露出来。

常见错误

  • 预期只可能发生 ValueError,却捕获了 Exception。
  • 把太多代码放进 try,导致很难定位错误来源。
  • 以为在 except 中 print 就够了;调用者可能还需要一个兜底值。
  • 用裸 except 把编程错误也吞掉。
  • 用裸 except 把 try 里的拼写错误一起藏起来。
轮到你

尝试把文本转换成整数,失败时使用 0 作为兜底,并打印结果。

动手试试

改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。

浏览器内 Python本地运行 · 无需账号
首次运行会从 CDN 下载 Pyodide。修改后点击“运行代码”。

练习本课

12 道题,作答后立即看到解释。

查看 12 道题答案开始练习
← 函数JSON 数据 →