☰
Python编程中常见的错误及其解决方法总结
2026/10/13 12:08:59 网站建设 项目流程

前言


新手面对报错时的第一反应,往往是「代码挂了,重写一遍」。但有经验的人会先做另一件事:把报错信息读完。Python 的报错信息里其实已经把「出了什么事、在哪一行、当时是什么类型」写得相当清楚,3.10 之后还进一步加了更精确的位置提示和「你是不是想写……」的猜测。


所以要先把本文的范围划清楚:这一篇只讲「怎么读报错」,不讲「怎么捕获」。try/except那套机制属于另一个话题,本文一次都不涉及。这里的目标是让你看到一个报错时,能在十秒内定位到「哪一行、哪类问题、下一步看什么」。


本文按报错的来源分四组讲:语法层(代码根本没被接受)、名字与属性与类型层(代码在跑,但用错了东西)、查找层(容器里没这个元素)、导入层(模块找不到)。每一组都给出真实的报错文本,逐行拆解。


一、报错信息的三段结构


不管哪类错误,Python 打印的格式都遵循同一套模板:


Traceback (most recent call last):
File "demo.py", line 7, in <module>
total = price + count
~~~~~~^~~~~~~
TypeError: unsupported operand type(s) for +: 'float' and 'str'

从上往下读三样东西:



  1. File "demo.py", line 7—— 出事的位置。有多个File块时,最后一个是直接出事的那一行,上面的是调用它的地方。

  2. 中间被引出来的那行源码,以及它下面那串^或~~~—— 精确指出这一行的哪一段出了问题。3.11 之后,这种「下划波浪线」标注变得很常见,它标出的就是参与运算的那个表达式。

  3. 最后一行—— 异常类型冒号后面是消息。类型决定「哪一类问题」,消息决定「具体是什么问题」。


记住这个口诀:看最后一行定类型,看倒数第二个 File 块定位置,看波浪线定范围。


二、语法层:SyntaxError 与 IndentationError


这两类错误有个共同特点:代码压根没开始执行。解释器在解析阶段就拒绝了,所以不会有「运行时」的信息,你看到的永远是「文件第几行写得不对」。


File "demo.py", line 1
print "hello"
^^^^^^^^^^^^^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

这是把 Python 2 的老代码拿到 Python 3 里跑的最典型症状。Python 2 里print是语句,可以不写括号;Python 3 里print是函数,括号是必须的。报错消息里那句猜测已经直接给了答案。顺带说,Python 2.7 已于 2020 年 1 月 1 日停止维护,这类改写是接手老项目时的必做项。


File "demo.py", line 3
return x
^^^^^^^^
SyntaxError: 'return' outside function

return只能出现在函数体内。看到这个报错,检查缩进层级——很可能上面那个def已经结束了,这一行掉到了模块顶层。


File "demo.py", line 4
total = a +
^
SyntaxError: invalid syntax

invalid syntax是最笼统的一类,但配上波浪线就很好办:它指向的位置,通常就是「读到这儿读不下去了」。像这个例子,加号后面缺了操作数,所以光标落在行尾。遇到invalid syntax,往指示位置的前面看,而不是往后看——错误往往出在它前一个记号上。


File "demo.py", line 5
print("done")
^
IndentationError: unexpected indent

File "demo.py", line 5
x = 1
IndentationError: expected an indented block

unexpected indent是「不该缩进却缩进了」,常见于从别处复制粘代码、多打了一个空格。expected an indented block是「该缩进却没缩进」,通常出现在if、for、def这类冒号之后。


还有一种是前两者的亲戚:


TabError: inconsistent use of tabs and spaces in indentation

同一个文件里混用了制表符和空格。肉眼看缩进是对齐的,但解释器数的是字符。统一用空格(PEP 8 建议 4 个空格)就能根治。


三、名字、属性与类型:NameError、AttributeError、TypeError


这一层是运行时错误,代码真的跑起来了,跑到某一行时发现「这个东西不对劲」。


NameError: name 'totl' is not defined. Did you mean: 'total'?

意思是「这个名字我从来没听说过」。常见成因有四种,按出现频率排:



  1. 拼写错误(如上面这个)。

  2. 变量在赋值之前就被使用。

  3. 忘了import,直接用json.dumps却从没import json。

  4. 作用域问题:在函数内部读取了一个只在别处定义的名字。


那半句Did you mean:是 Python 3.10 起加入的,解释器会在当前作用域里找相似名字,非常省事。


它的一个变体要单独认:


UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

名字确实存在,但它在函数里被赋过值,于是成了局部变量;而在被读取的那一刻,这个局部变量还没有值。典型场景是函数里既读又写同一个名字,却在读的时候还没走到写的那一步。这句话的措辞在不同版本里不一样:较新的版本会直说「不能访问尚未绑定值的局部变量」,更早的版本写的是「局部变量在赋值之前被引用」。两种说法指的是同一件事,看到旧措辞不要以为换了机制。


AttributeError: 'str' object has no attribute 'append'. Did you mean: 'extend'?

意思是「str这个类型的对象上没有append这个方法」。读法是:先看类型,再看方法名。'str'说明你手上是一个字符串,而字符串是不可变的,没有追加元素的方法——append是列表的方法。这里的根因几乎总是「变量在半路被赋值成了别的类型」。


TypeError: unsupported operand type(s) for +: 'int' and 'str'

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

TypeError: 'int' object is not callable

unsupported operand type(s) for +是「这两个类型不能做这个运算」;can only concatenate str (not "int")是它针对字符串加法的专门措辞;'int' object is not callable则是「你对一个整数用了调用语法」,最常见的原因是变量名遮蔽了函数名——比如先定义了def list(): ...,后来又list = [1, 2],于是list()就调不动了。


TypeError的读法固定为三步:看运算符或方法名、看两侧(或对象)的类型、回到那一行确认变量到底是什么。


四、查找与导入:KeyError、IndexError、ModuleNotFoundError


KeyError: 'name'

字典里没有name这个键。报错消息里被引号包住的那段就是这个键本身,非常直白。如果键是字符串且里面有空格或特殊字符,有时会看到双重引号,把内容按原样读出来即可。排查时通常要回头确认「写进去的时候用的是哪个键」——是"name"还是"Name",是字符串还是数字。


IndexError: list index out of range

下标超出了序列范围。注意它不带具体数值,所以要自己回到那一行看用的是哪个下标。常见成因:用len(lst)当下标(最大值应该是len(lst) - 1)、迭代过程中删元素导致长度变了、把空列表当成有内容的列表来取。


ModuleNotFoundError: No module named 'requests'

导入的模块找不到。按顺序排查:



  1. 模块名拼写是否正确(模块名大小写敏感)。

  2. 标准库还是第三方库——requests不是标准库,没装就是没有。装法是pip install requests,具体包名以该库官方文档为准。

  3. 是不是自己写的文件,比如同目录下有utils.py却import util。若是包,还要看它有没有__init__.py、是否在sys.path里。

  4. 虚拟环境是否激活,装在了另一个环境里是最常见的「明明装了却说没有」。


ModuleNotFoundError是ImportError的子类,所以在老的英文资料里常看到ImportError: No module named ...这种写法,那是更早版本的措辞。


下面这张表把最常见的一批报错压成速查表:




报错一句话含义先看什么



SyntaxError语法没通过,代码没执行波浪线位置的前一个记号

IndentationError缩进层级不对冒号后的缩进、是否混用 Tab

NameError名字不存在拼写、是否import过

UnboundLocalError局部变量还没赋值就被读函数内的赋值语句位置

TypeError类型不支持这个操作运算符两侧的类型

AttributeError对象没这个属性/方法对象实际是什么类型

KeyError字典没这个键键的拼写与类型

IndexError下标越界下标表达式与序列长度

ModuleNotFoundError模块找不到是否安装、环境是否正确



常见坑点


1. 只看第一行Traceback就下结论


❌ 一看到Traceback (most recent call last):就以为是第一段File出了问题。 ✅ 先看最后一行确定异常类型,再看最后一个File块确定直接出事的行。


2. 把invalid syntax的位置当成错误所在


❌ 紧盯着波浪线指向的那个字符反复检查。 ✅ 解析器是「读到读不动了」才报错,问题常常在它前面一个记号:少个括号、少个逗号、少个冒号。


3. 以为NameError一定是拼错


❌ 把名字逐字母核对三遍,仍找不到原因。 ✅ 检查三件事:有没有import、是不是在赋值之前使用、是不是在函数里访问了不该访问的作用域。


4. 把AttributeError的成因归到方法不存在


❌ 去查文档确认append到底叫什么。 ✅ 先看报错里的类型('str' object),确认这个变量实际是什么类型,多数时候是类型在半路变了。


5. 用len()当最后一个下标


❌lst[len(lst)],直接越界。 ✅ 最后一个下标是len(lst) - 1;for循环里用enumerate拿下标更稳。


6. 看到ModuleNotFoundError就反复重装


❌ 一遍遍pip install,问题照旧。 ✅ 先确认当前用的是哪个 Python 环境、装在哪个解释器下,虚拟环境没激活是高频原因。


7. 把 Python 2 的老代码直接跑在 Python 3 上


❌print "x"、except X, e、raise X, y、dict.iteritems()、xrange这类写法直接搬过来。 ✅ 分别为:print("x");except X as e;raise X(y);.items();range。Python 2.7 已于 2020 年 1 月 1 日停止维护,老写法只是为了读懂遗留代码。


8. 遇到报错就吞掉,靠猜改代码


❌ 加一句pass让程序继续跑,报错看不到了,问题也还在。 ✅ 先把报错信息读懂、定位到具体那一行,再决定怎么改;报错不是噪音,它是现场记录。


总结




层次代表报错读法要点



语法层SyntaxError、IndentationError、TabError代码未执行;看指示位置的前一个记号、看缩进

名字/属性/类型层NameError、UnboundLocalError、AttributeError、TypeError看类型、看方法名、看变量在半路被改成了什么

查找层KeyError、IndexError键直接写在消息里;下标要自己回查

导入层ModuleNotFoundError拼写、是否安装、环境是否正确



读报错是一项可以练出来的技能,而且性价比极高:必须先定位到那一行,才谈得上修。最后一行给你类型,倒数第二个File块给你行号,那串波浪线给你范围——把这三样读全再动手,多数问题不必靠猜。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询