写代码报错是常态,不是失败。真正的技能是会修。前面几篇教你写,这一篇教你在写错时怎么找回来。
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>、andvsor) - 条件里赋了错的值
- 缩进错位,导致某行在错误的时机执行(下面的 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 写的代码尤其要自己验,因为它最爱犯的就是那种能跑、不报错、但结论是反的逻辑错误。