Skip to content

第 12 章 · 报错急救手册与调试 ​

本章目标 ​

遇到报错不慌:会读、会查、会自己定位问题。

12.1 排查四步法 ​

  1. 读最后一行:错误类型 + 原因(这一行信息量最大)
  2. 往上找行号:报错发生在哪个文件的第几行
  3. 看那一行的上下文:变量是什么类型?是不是 None?拼写对不对?
  4. 缩小范围:把可疑代码单独拿出来,用 print 打印中间结果

一句经验:90% 的报错和你以为的地方不是同一个地方 —— 报错在 A 行,问题往往在 A 行之前。

12.2 高频报错对症下药 ​

SyntaxError: invalid syntax ​

python
print("hi"        # ❌ 少了右括号
if x == 1         # ❌ 少了冒号

检查:括号引号成对、冒号、中英文标点(, vs , 是重灾区)。

IndentationError: unexpected indent ​

缩进不一致:同一层代码必须空格数相同,别混用 Tab 和空格。编辑器里开启"Tab 转 4 空格"。

NameError: name 'x' is not defined ​

变量名拼错,或者用了还没赋值的变量。

TypeError: can only concatenate str (not "int") to str ​

python
age = input("年龄:")
print("明年 " + age + 1)      # ❌ 字符串 + 数字
print(f"明年 {int(age) + 1}")  # ✅

ValueError: invalid literal for int() with base 10: 'abc' ​

int() 收到了不能转的字符串。转换前用 .isdigit() 判断,或用 try 接住。

KeyError: 'phone' ​

字典没有这个键。改用 user.get("phone", "未填"),或先 if "phone" in user。

IndexError: list index out of range ​

下标越界。检查 len(lst),注意下标从 0 开始。

AttributeError: 'str' object has no attribute 'append' ​

用错了方法(字符串没有 append)。用 += 拼字符串,或改用列表。

ModuleNotFoundError: No module named 'requests' ​

没装 / 装到了别的环境。激活虚拟环境后 python -m pip install requests。

UnboundLocalError: cannot access local variable 'x' ​

函数里既想读又想改全局变量,忘了写 global x。

FileNotFoundError / UnicodeDecodeError ​

路径不对 / 编码不对 —— 见第 07 章。

12.3 三种调试手段 ​

① print 大法(最朴素最有效) ​

python
def calc(items):
    print("DEBUG 传入:", items, type(items))     # 看输入
    total = sum(items)
    print("DEBUG 合计:", total)                  # 看中间结果
    return total / len(items)

② assert 断言(给"我认为一定成立"的假设上保险) ​

python
def average(nums):
    assert len(nums) > 0, "列表不能为空"
    return sum(nums) / len(nums)

假设不成立就立刻报错,比让错误数据往下跑要好得多。

③ 断点调试 breakpoint() ​

python
def buggy(data):
    breakpoint()          # 程序在这里暂停,进入调试器
    return data["value"] * 2

运行到这一行会停下,可以看变量、单步执行。VS Code 里更直观:行号左边点一下打红点,按 F5 启动调试。

调试器常用命令(终端里):p 变量名 查看值、n 下一行、c 继续、q 退出。

12.4 常见错误模式与成因 ​

现象常见成因定位方法
报错行看起来没问题错误源自前面几行的变量或类型从报错行往前逐行检查变量与类型
改了代码但结果没变文件未保存,或运行的另一个文件确认保存路径与运行命令一致
结果时对时错字符串与数字混用;可变对象共享print(type(x));检查默认参数与类属性
Traceback 很长只需末行错误类型与触发位置看最后一行 + 对应文件行号
循环越来越慢循环内重复构造大对象或反复 IO把不随循环变化的部分移到循环外

12.5 附:"写成什么样才会报这个错"速查 ​

上面每条已给了成因;这里把最常撞的几种,用 2 行代码直接演示"错误写法 → 正确写法":

python
d = {"a": 1}
# KeyError:   print(d["b"])                  → print(d.get("b")) 或 if "b" in d
# IndexError: print([1, 2][5])               → 先 len() 判断或改用 for 遍历
# ValueError: print(int("abc"))              → if s.isdigit(): int(s)
# TypeError:  print("18" + 1)                → print(int("18") + 1)
# NameError:  print(totl)                    → 变量名拼写/是否已赋值
# AttributeError: "abc".push("d")             → 查清方法名:"abc".upper()
# ModuleNotFoundError:import pandas(未装)   → python -m pip install pandas(已激活 venv)
# FileNotFoundError:open("不存在.txt")         → 用 Path(__file__).parent 拼绝对路径
# ZeroDivisionError: 100 / 0                 → 计算前 if b != 0:

count = 0
def f():
    count += 1        # ❌ UnboundLocalError:函数里改外部变量需先写 global count
                      # (或把赋值改成 count = 1,那就是局部变量了)

以及两类"最难自己发现"的:

python
# ① 缩进错误:同一层代码必须对齐
if True:
    print("A")
      print("B")     # ❌ IndentationError: unexpected indent

# ② 中文标点:看起来一样,机器不认
# print("你好")     ❌ 引号是中文的
# print("你好")     ✅ 英文引号

12.6 练一练 ​

  1. 下面这段代码有 3 个错误,找出来并修好(改完应该输出 80.0):
python
def get_avg(scores)
    total = 0
    for s in scores:
    total = total + s
    return total / len(scores)

print(get_avg([90, 80, 70)
  1. 故意写一段会报 KeyError 的代码,用 try/except 处理掉。
  2. 用 breakpoint() 或 VS Code 断点,观察一个循环里变量的变化过程。

本章关键词 ​

Traceback、错误类型对照、print 调试、assert、breakpoint()、VS Code 调试

试图用自己的方式,重新定义属于我的人生