客人在数量栏里打了「三」,收银台就在队伍中间卡住了。这个失败完全可以预料,恢复也该如此。哪些异常值得接住,哪些应该让程序干脆停下来?
错误是一种信息。有些是应该修复的 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 运行时,可能需要几秒钟。
练习本课
12 道题,作答后立即看到解释。