写代码报错是常态,不是失败。真正的技能是会修前面几篇教你写,这一篇教你在写错时怎么找回来。

Python 的错误分三类,危险程度递增:语法错误 → 异常 → 逻辑错误。理解这三类的区别,是高效调试的起点。


先看全景:三类错误对照

类型会报错吗啥意思危险度
语法错误 Syntax Error✅ 报,且给行号Python 语法写错了(漏冒号、漏引号)🟢 低——报错直接告诉你
异常 Exception✅ 报,运行时崩语法对,但跑不动(用了没定义的变量等)🟡 中——报错给线索
逻辑错误 Logic Error不报错代码合法、能跑,但结果是错的🔴 高——静默,最难抓

核心认知:报错是好事。 最可怕的不是报错的代码,而是安安静静给你错误答案的代码——那就是逻辑错误。对安全脚本尤其致命:一个判断写反,可能让该拦的没拦、该报警的没报警。


语法错误:漏标点之类,报错带行号

最低级也最好修——违反 Python 语法本身。漏个冒号、漏个引号、漏个括号。

message = "You are debugging a syntax error
print(message)

报错:SyntaxError: EOL while scanning string literal(EOL = end of line,行尾)。它明确告诉你第 1 行——字符串少了个结尾引号。补上即可。

常见变体:

  • SyntaxError: invalid syntax —— 泛指语法错
  • unexpected EOF while parsing —— EOF = end of file,通常是括号没闭合
  • IndentationError —— 缩进错(它是 SyntaxError 的子类),条件/循环缩进不对时常见

语法错误几乎不用动脑:报错说哪行就看哪行。


异常:语法对,但跑不动

Exception——代码语法完全正确,但运行时执行不下去。最常见的几种,认得出报错名就行:

报错名什么情况例子
NameError用了没定义的变量/函数print(unusual_logins) 但从没给它赋值
IndexError列表索引越界usernames 只有 0/1/2,却访问 usernames[3]
TypeError数据类型用错拿字符串和整数做加法("5" + 3)
FileNotFoundError打开不存在的文件路径写错、文件没创建
username = "elarson"
total_logins = 75
print("Total logins:", total_logins)
print("Unusual logins:", unusual_logins)   # ← NameError: unusual_logins 没定义

TypeError 是 vibe-coder 高频坑:从日志、API 取出来的数字默认是字符串,直接拿去算就崩。这正是type() 那一节强调”先查类型”的原因。


逻辑错误:不报错,但答案是错的(最危险)

代码合法、能跑、不报错——但做的不是你想要的事。Python 看不出问题,只有你对照”预期 vs 实际”才能发现。

经典例子:把 < 写成 >=

login_attempts = 5
if login_attempts >= 5:
    print("用户还没达到最大登录次数。")   # ← 输出了这句
else:
    print("用户已达到最大登录次数。")

登录次数已经是 5(达到上限了),却打印”还没达到”。条件本该是 login_attempts < 5代码一声不吭地给了你反的结论——在真实场景里,这就是”该锁的账号没锁”。

逻辑错误的常见来源:

  • 用错比较运算符(>= vs >and vs or)
  • 条件里赋了错的值
  • 缩进错位,导致某行在错误的时机执行(下面的 print 大法例子就是这种)

三种调试策略

1. 读报错信息(语法错误/异常首选)

Python 一次只报第一个错误,从上往下。修掉再跑,它再报下一个。语法错误和异常的报错通常已经够你定位了——别急着改,先读完报错

2. print 大法:在关键位置插临时打印

逻辑错误不报错,得靠你自己”看代码在每一步干了什么”。最朴素也最有效的办法:到处插临时 print,标上行号和说明

看这段”把新用户加进白名单、但已在名单里的不重复加”的代码——它有逻辑 bug:

new_users = ["sgilmore", "bmoreno"]
approved_users = ["bmoreno", "tshah", "elarson"]
def add_users():
    for user in new_users:
        if user in approved_users:
            print(user, "already in list")
        approved_users.append(user)     # ← bug:不管在不在,都 append 了
add_users()
print(approved_users)

虽然打印了 “bmoreno already in list”,但 bmoreno 还是被重复加了一次。插 print 定位:

def add_users():
    for user in new_users:
        print("line 5 - inside for loop")
        if user in approved_users:
            print("line 7 - inside if statement")
            print(user, "already in list")
        print("line 9 - before .append method")   # ← 这句打印了两次!
        approved_users.append(user)

观察输出:“line 9 - before .append” 打印了两次,说明两个用户都走到了 append——即使 bmoreno 已经在名单里。这就锁定了 bug:append 这行应该放进 else 里,只在”不在名单”时才执行。

print 大法的精髓:用打印验证”代码真实走过的路径” vs “你以为它走的路径”。两者不符的地方,就是 bug。调试完记得删掉这些临时 print。

3. 调试器 + 断点(IDE 里)

在 IDE(VS Code、PyCharm 等)里有更专业的工具:调试器(debugger)

  • 断点(breakpoint) —— 在某行打个标记,程序跑到这里暂停,让你检查当时的状态
  • 看变量值 —— 单步执行,实时看每个变量怎么变。对逻辑错误特别有用——能精确看到”变量在哪一步被改成了意外的值”

比 print 大法更系统,不用反复改代码插打印。


给 vibe-coder:AI 既是 bug 来源,也是调试助手

这是这门课最该划重点的一节。

AI 写的代码,最容易藏”逻辑错误”。 语法错误和异常 AI 基本不犯(它语法很熟),但逻辑错误——把 > 写成 >=、条件判断反了、边界没处理对——AI 经常犯,而且代码能跑、不报错,看起来人模人样。

所以:

AI 调试助手(如 Gemini Code Assist、Copilot)能帮你但你必须
解释报错、建议改法、对话式答疑自己验证它的输出——它可能给出不准确、不安全的代码
加速找语法错误/异常逻辑保持警惕——AI 看不出”这不符合你的本意”
当 co-pilot(副驾)当机长——最终代码的正确性和安全性是你的责任

一条铁律,呼应整个 供应链那篇的精神:AI 是副驾,不是机长。 任何 AI 生成的代码,跑之前先问自己——“它真的在做我要它做的事吗?” 尤其安全相关的判断逻辑,亲自拿几组数据测一遍(包括边界值:0、空、超限),别让一个静默的逻辑 bug 变成生产事故。


一句话总结

三类错误:语法错误(报错带行号,好修)、异常(语法对但跑不动,看报错名)、逻辑错误(不报错但结果错,最危险)。调试三招:读报错、print 大法验证执行路径、IDE 调试器+断点。最该记住的是——报错不可怕,沉默的逻辑 bug 才可怕;AI 写的代码尤其要自己验,因为它最爱犯的就是那种能跑、不报错、但结论是反的逻辑错误。