学径XUEJING · 个人学习平台通知设置

课程库 AI 主线 Python 进阶语法 › 第 5

错误与异常处理 —— try/except:程序的安全气囊

普通课60 分钟

本课导读

一路走来你已经见过不少红字:KeyErrorIndexErrorTypeError……阶段 0 的态度是"读懂它、修好它"。本课升级认知:报错不是敌人,是信使——先学会系统地读 traceback,再学 try/except 给程序装安全气囊:该扛住的扛住(用户输错一个数,程序不能整个瘫掉),该暴露的暴露(数据错了就要响亮地报出来)。预计 60 分钟。

一、读懂 traceback:从最后一行往上看

先故意制造一次事故,看清报错信息的结构:

Python

运行后你会看到一段 traceback(调用轨迹)。读法口诀:先看最后一行,再往上找自己的代码

  • 最后一行是"诊断书":KeyError: '摩卡'——错误类型 + 具体线索
  • 往上的每一层是"案发路线":错误发生在哪一行、这行又是被谁调用的
  • 排查时优先盯自己写的那几行,库内部的层级多数时候不用管

常见错误类型速查(都是老熟人了,认个全家福):

报错人话翻译典型场景
KeyError字典里没这个键menu["摩卡"]
IndexError下标超出范围nums[99]
TypeError类型对不上"3" + 5
ValueError类型对但值不合法int("abc")
NameError变量根本不存在拼错变量名
ZeroDivisionError除以零total / 0

二、try/except:安全气囊

有些错误你预料得到但拦不住——最典型的是用户输入。运营小林写了个折扣计算器,用户输入"八折"而不是数字,程序当场崩溃。try/except 出场:

Python

结构拆解:

  • try: 块里放可能出事的代码——出事瞬间跳出,直奔 except
  • except 错误类型: 接住指定类型的错误,执行善后代码;程序不崩,继续往下走
  • 可以叠多个 except,不同错误不同处理——就像医院分诊

提示

类比记忆:try/except 是汽车安全气囊——正常行驶时完全无感(try 里没出错就直接跳过 except),撞击瞬间弹出保护(错误被接住,程序不散架)。注意:气囊是为"预料得到的意外"设计的,不是让你闭眼开车。

三、气囊的正确姿势与三个坑

坑一:裸 except 一把梭。

try:
    result = do_something(data)
except:              # 什么错都吞掉 —— 千万别这么写
    print("出错了")

except: 不写类型会吞掉一切错误——包括你代码里真正的 bug(比如变量名拼错的 NameError)。bug 被无声吞掉,比崩溃可怕一百倍。永远写明错误类型

坑二:try 的范围过大。 只把危险的那一两行放进 try——范围越小,你越清楚接住的是什么错。

坑三:能预防就不要抢救。 上一课的 .get()in 探路都是预防;try/except 是抢救。查字典这类能提前检查的,预防更清晰;用户输入、文件读取这类"检查了也可能出意外"的,才用气囊。

Python

注意分工:类型转换失败交给 except(预料外),数值范围检查用 if(预料内)——气囊和方向盘各干各的活。

四、主动报错:raise 一嗓子

反过来,有时你希望程序响亮地报错而不是默默容忍。函数收到不合法的参数时,可以主动 raise

Python

raise 错误类型("说明文字") 主动抛出错误。看起来像自找麻烦,其实是把事故拦在案发第一现场——不然一个错误的折扣率会一路算下去,最后在几百行外炸出个莫名其妙的结果,排查半天。

说明

一句话总结错误哲学:对外来的意外要宽容(try/except 接住),对内部的错误要严格(让它响亮地炸出来,甚至主动 raise)。这个分寸感,是新手到熟手的重要一跃。

✍️ 练习

练习 1:报错侦探

不运行,先说出下面每一行各会引发哪种错误,再逐行取消注释验证。

Python
✅ 参考答案

依次是:IndexError(下标 5 超范围)、KeyError(没有 age 键)、ValueError(类型对但值不合法)、TypeError(列表和数字不能相加)。能秒答这四个,traceback 对你就没有威慑力了。

练习 2:健壮的平均分

avg 函数在列表为空时会崩(除以零)。用 try/except 修好它:空列表时返回 0。

Python
💡 提示

把除法那行放进 try,except 接 ZeroDivisionError 返回 0。(也可以用 if 预防——两种都写写,体会"预防 vs 抢救"。)

✅ 参考答案
def avg(scores):
    try:
        return sum(scores) / len(scores)
    except ZeroDivisionError:
        return 0

# 预防式写法(本例中更推荐——空列表是能提前检查的):
def avg2(scores):
    if len(scores) == 0:
        return 0
    return sum(scores) / len(scores)

print(avg([80, 90, 100]), avg([]))
print(avg2([80, 90, 100]), avg2([]))

练习 3:批量转换幸存者(挑战)

一批用户填的年龄有好有坏。写代码把能转成整数的收进 valid 列表,转不了的收进 rejected 列表,最后打印两个列表。程序全程不能崩。

Python
💡 提示

for 遍历,循环体里 try 转换 + append 到 valid,except ValueError 时 append 到 rejected。气囊装在循环里面,一个坏数据才不会中断整批处理。

✅ 参考答案
raw_ages = ["25", "abc", "31", "", "40岁", "18"]

valid = []
rejected = []
for raw in raw_ages:
    try:
        valid.append(int(raw))
    except ValueError:
        rejected.append(raw)

print(f"有效:{valid}")
print(f"待人工处理:{rejected}")

这个"逐条 try、坏的隔离、好的继续"模式,是数据清洗的标准动作——pandas 课里你会重逢它。

📝 随堂测验

随堂测验0 / 5 题正确

1. 看 traceback 的第一步是?

2. int("abc") 会引发哪种错误?

3. 为什么不该写不带类型的裸 except: ?

4. “查字典前先用 in 探路”和“try/except 包住取值”,选择的原则是?

5. 函数里写 raise ValueError("折扣率不合法") 的目的是?

本课小结

  • traceback 读法:最后一行看诊断,往上找自己的代码;六大常见错误类型认全
  • try/except 错误类型:安全气囊——接预料得到的意外;永远写明类型,try 范围要小
  • 能预防(in/.get/if 检查)就不抢救;用户输入、文件读取才是气囊的主场
  • raise:对不合法的输入主动报错——错误越早暴露越好修

下一课聊聊"代码的仪表盘"——命名、注释与 PEP8:让三个月后的你还能看懂今天的代码。

划选正文任意文字可高亮、批注或加入复习卡

讨论

载入中…