Python 在很多方面都很宽容,但它的解析器并非如此。一个 SyntaxError 会在任何一行代码执行之前就停止程序,当修复只是一个缺失的字符时,这令人沮丧。好消息是,同样的几个错误几乎涵盖了你会遇到的所有语法错误,一旦你能够一眼识别它们,就能在几秒钟内修复。
本指南将介绍最常见的 Python 语法错误,以及密切相关的 IndentationError、TabError 和 NameError 案例,这些错误出于相同的原因让人犯难。每个部分都展示了一个错误片段、Python 打印的错误信息,以及更正后的版本。每个片段都是真实可复制的,因此你可以在自己的解释器中重现错误并确认修复效果。
Python 中的语法错误是什么?
语法错误意味着你的代码违反了语言的语法规则,因此解释器无法将其解析为可运行的内容。由于解析发生在执行之前,程序从不启动:没有任何输出,没有副作用,你只会得到一个以 SyntaxError 结尾的回溯信息,并有一个插入符号指向问题标记所在位置或紧随其后的位置。
以下是典型示例,一个没有闭合括号的调用。
print("Hello, Python!"
Python 到达文件末尾时仍在等待闭合调用的 ),因此在输入末尾报告了问题。
SyntaxError: '(' was never closed
有一点值得在你的思维模型中尽早纠正:语法错误与运行时错误不同。语法错误在解析时被捕获,完全阻止程序启动。运行时错误,如除以零或打开不存在的文件,发生在语法有效的程序运行过程中。本指南关注的是第一种,即那些从不让你的代码运行的错误。
常见 Python 语法错误及修复方法
以下各节大致按照你遇到这些错误的顺序排列。每个错误都小而独立,因此可以直接跳到你当前正在盯着的错误消息处。
1. 缺少或不匹配的括号
括号用于分组表达式和调用函数,必须成对出现。未闭合的开括号是最常见的语法错误之一,尤其在较长的条件语句中,闭合括号很容易被遗漏。
if (x > 10: print("x is greater than 10")
Python 期望在冒号之前有一个闭合括号,并会告知你这一点。
SyntaxError: closing parenthesis ':' does not match opening parenthesis '('
修复方法是确保每个开括号都有相同类型的闭合括号与之匹配。在冒号前加上闭合括号,条件语句就能正常解析。同样的规则适用于方括号 [] 和花括号 {};能够高亮匹配括号的编辑器通常能帮你发现大多数这类错误。
if (x > 10): print("x is greater than 10")
2. 控制语句中缺少冒号
Python 中每个复合语句头都以冒号结尾:if、elif、else、for、while、def、class、try 等等。当你从使用花括号的语言转来时,遗漏冒号是典型的错误。
if x > 5 print("x is greater than 5")
现代 Python 会直接指出缺少的标记。
SyntaxError: expected ':'
在头部行的末尾添加冒号。每当你写一个开始新缩进块的语句时,你的眼睛应该自动检查末尾的冒号。
if x > 5: print("x is greater than 5")
3. 期望缩进块(IndentationError)
Python 使用缩进而非花括号来标记代码块,因此每个 if、循环或函数的主体必须缩进。写一个带冒号的头部,然后忘记缩进下一行,解析器就没有块可以附加了。
for i in range(5): print(i)
解释器报告说它期望在循环头之后有一个缩进块。
IndentationError: expected an indented block after 'for' statement on line 1
将主体缩进四个空格,这是 Python 的标准约定。缩进在这里是语法,而非风格偏好:Python 正是通过它来知道哪些行属于循环的。
for i in range(5): print(i)
4. 意外缩进(IndentationError)
相反的错误是对一行本应与相邻行处于同一层级的代码进行缩进。同一块内的缩进必须一致,因此某一行上多出的一两个空格会破坏解析。
def example(): print("First line") print("Second line")
第二个 print 无缘无故地比第一个缩进更多,Python 会拒绝它。
IndentationError: unexpected indent
将同一块中的每个语句对齐到同一列。每级选择四个空格并保持统一,你的编辑器的自动缩进通常会帮你保持一致。
def example(): print("First line") print("Second line")
5. 混用制表符和空格(TabError)
一种特别隐蔽的缩进错误是在同一块中混用制表符和空格。两者在屏幕上看起来可能相同,但 Python 将它们视为不同字符,并会引发专用的 TabError。在下面的片段中,第一个主体行使用空格,第二个使用制表符。
def greet(name): message = "Hi " + name return message
尽管两行在视觉上对齐,Python 无法判断它们是否处于同一层级。
TabError: inconsistent use of tabs and spaces in indentation
选择 PEP 8 推荐的空格,永不混用。配置你的编辑器在按 Tab 键时插入空格,并对任何已有混用的文件进行搜索替换。将两个主体行都用四个空格重新缩进即可消除错误。
制表符与空格的错误在你打开空白字符渲染之前是不可见的。大多数编辑器都有"显示空白字符"或"渲染控制字符"的切换选项,能将制表符和空格显示为不同的标记。打开一次,这类错误就不再神秘了。
6. 扫描字符串字面量时遇到行尾(EOL)
字符串必须在同一行用匹配的引号开闭。忘记闭合引号,或用错误类型的引号闭合,Python 就会在仍处于字符串内时到达行尾。
name = 'John"
字符串用单引号开始但用双引号结束,因此它从未被终止。
SyntaxError: unterminated string literal (detected at line 1)
在两端使用相同的引号字符。如果你的文本包含引号,用另一种引号包裹字符串,例如 "O'Brien",或对跨多行的文本使用三引号字符串。较旧的 Python 版本将同一错误表述为 EOL while scanning string literal,但原因完全相同。
name = 'John'
7. 扫描时遇到文件末尾(EOF)
密切相关的是文件末尾版本:一个开括号或引号在文件结束之前从未闭合。这是前面提到的未闭合调用的情形,它值得单独一节,因为错误消息指向了不同的原因。多行结构缺少闭合符通常是最常见的罪魁祸首。
numbers = [1, 2, 3 print(sum(numbers))
由于列表从未闭合,Python 持续读取,将下一行视为列表的一部分,并跑到文件末尾。
SyntaxError: '[' was never closed
在结构结束处闭合括号。当错误指向文件末尾时,真正的问题几乎总是在上方几行有一个从未匹配的开括号,因此从插入符号向上扫描。
numbers = [1, 2, 3] print(sum(numbers))
8. 无效赋值:= 与 ==
单个 = 赋值;双 == 比较两个值。在需要比较的地方使用赋值,例如在 if 条件中,Python 会拒绝它,因为你不能在普通条件测试中赋值。
if x = 10: print("x is 10")
近期版本的 Python 会明确说明你可能的意图。
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
用 == 进行比较,将 = 留给赋值。如果你确实想在表达式中赋值,那正是海象运算符 := 的用途,但对于普通条件,你几乎总是意味着 ==。
if x == 10: print("x is 10")
一旦你的解析器运行无误,抓取器的难点就在于抓取:渲染 JavaScript 页面并轮换 IP 以避免被封锁。Crawling API 通过单次请求处理两者并返回完整 HTML,让你的无错误 Python 专注于解析数据,而非与无头浏览器集群和代理池博弈。
9. 将保留关键字用作变量名
Python 为语言本身保留了 for、if、class、return 和 import 等词。尝试将其用作变量名,解析器就会在期望标识符的地方看到一个关键字。
for = 10
给 for 赋值是无效的,因为 for 只能开始一个循环。
SyntaxError: invalid syntax
选择一个非保留的名称,最好是描述该值的名称。如果你不确定某个名称是否被占用,Python 自带的 keyword 模块列出了每个保留词;通过 keyword.kwlist 检查一个名称即可立即确认。
import keyword num_for = 10 print("for" in keyword.kwlist) # True, so it is off limits
10. 逗号缺失或位置错误
逗号分隔列表、元组、字典和参数列表中的项目。在两个字符串字面量之间遗漏逗号,Python 会尝试将它们读作单个表达式,这是无效的。
items = ["apple" "banana", "cherry"]
Python 指向第二个字符串,在那里期望有一个逗号。
SyntaxError: invalid syntax. Perhaps you forgot a comma?
在每个项目之间加上逗号。这个错误经常出现,因为 Python 会静默地连接两个相邻的字符串字面量,所以 "apple" "banana" 有时不会报错而产生 "applebanana",这是一个令人困惑的 bug 而非干净的报错。始终明确地用逗号分隔你的项目。
items = ["apple", "banana", "cherry"]
11. 不正确的 import 语句
Import 有自己的语法。忘记命名你要导入的内容,from ... import 形式就会不完整。
from math import
解析器到达行尾时仍在期待一个要导入的名称。
SyntaxError: invalid syntax
命名你要导入的内容,或者导入整个模块。以下两种形式都是有效的:导入模块并用点符号访问,或从中提取特定名称。
import os import sys from math import sqrt
12. print 作为语句而非函数
在 Python 2 中,print 是一个语句,不带括号即可书写。在 Python 3 中它是一个普通函数,因此旧形式是语法错误。这仍然会困扰那些从旧教程中复制示例的人。
print "Hello, Python!"
Python 3 甚至在消息中给出了建议的更正。
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?
将参数用括号包裹,就像调用任何其他函数一样。同样的规则适用于 exec,它在 Python 3 中也变成了函数。
print("Hello, Python!")
13. 拼写错误导致的 NameError
最后这个严格来说不是语法错误:代码可以正常解析,因此它在运行时而非解析时失败。它被放在这里是因为原因属于同一类小失误,而且修复同样快速。拼错变量名,Python 就找不到它了。
message = "hello" print(mesage)
名称 mesage 从未被定义,因此 Python 引发了一个 NameError 并有帮助地猜测了你的意思。
NameError: name 'mesage' is not defined. Did you mean: 'message'?
修正拼写,使名称与其定义匹配。由于这个错误只在实际运行到那一行时出现,它可能隐藏在你很少触及的分支中,这是分小块测试代码而非一次性测试整个脚本的另一个理由。
message = "hello" print(message)
如何避免 Python 语法错误
你永远不会完全停止犯这些错误,但你可以在运行任何代码之前捕获几乎所有错误。几个习惯能完成大部分工作。
- 使用带有语法高亮的编辑器。 VS Code、PyCharm 以及 Thonny 等轻量编辑器会在你输入时标记不匹配的括号、错误缩进和损坏的字符串,因此大多数错误永远不会到达解释器。
-
选择四个空格,永不混用制表符。 配置你的编辑器在按 Tab 键时插入空格。一致的缩进单独就能消除整个
IndentationError和TabError系列。 - 运行 linter。 Pylint、Flake8 和 Ruff 等工具在你运行代码之前扫描语法问题、缺失逗号和未定义名称,大多数都可以直接插入你的编辑器。
- 读取错误消息和插入符号。 现代 Python 精确指向有问题的标记,通常还会建议修复方法。行号是 Python 注意到问题的地方,对于未闭合的括号,通常在实际原因下方一两行。
- 分小块测试。 每次运行几行而非整个脚本。当只有少数几行新代码可能有责任时,错误更容易定位。
这些习惯在你从玩具代码进入真实项目(包括抓取器)时也能保持代码整洁。如果你想要一个将可运行 Python 付诸实践的完整演练,请参阅 如何用 Python 抓取网站,以及你将会用到的工具概览,请参阅 最佳 Python 网络抓取库。
核心要点
- 语法错误在程序运行前就停止它。 与运行时错误不同,它们在解析时被捕获,因此在修复之前什么都不会执行。
- 大多数语法错误是标点问题。 缺少冒号、未闭合的括号、不匹配的引号或遗漏的逗号占了绝大多数情况。
- 缩进是语法,不是风格。 一致地缩进块体,使用四个空格,永不混用制表符和空格,可以避免整个 IndentationError 和 TabError 系列。
- = 赋值,== 比较。 在需要另一个的地方使用其中一个是常见的失误,现代 Python 现在会直接指出。
- 读取消息和插入符号。 近期 Python 版本精确命名有问题的标记并通常建议修复方法,对于文件末尾错误,真正的原因通常是上方未闭合的开括号。
常见问题
Python 中语法错误和运行时错误有什么区别?
语法错误违反了语言的语法规则,因此解释器无法解析你的代码,程序从不启动。运行时错误发生在有效程序运行时,当某个操作失败时,例如除以零或打开不存在的文件。语法错误在执行之前被捕获;运行时错误在执行期间被捕获。
为什么 Python 如此重视缩进?
Python 使用缩进而非花括号来定义代码块,这使代码保持可读性和统一性。由于空白字符就是语法,不一致的缩进是真正的错误,而非风格问题。每级选择四个空格并让编辑器强制执行,缩进就不再是 bug 的来源了。
如何修复"SyntaxError: unexpected EOF while parsing"?
该消息表示一个开括号、圆括号或引号从未闭合,因此 Python 跑到文件末尾仍在等待闭合符。从错误指向的行开始,向上扫描未匹配的开括号。较新的 Python 版本更具体地描述同一问题,例如 '(' was never closed,告诉你确切要追踪哪个字符。
为什么我的代码看起来正确缩进却出现 TabError?
因为制表符和空格在屏幕上看起来相同,而 Python 将它们视为不同字符。如果一个块中的某行使用制表符而另一行使用空格,尽管视觉上对齐,缩进也是不一致的。在编辑器中打开空白字符渲染,将所有内容转换为空格,错误就会消失。
如何在运行代码前捕获语法错误?
使用带有语法高亮的编辑器,以及 Pylint、Flake8 或 Ruff 等 linter。这些工具在你输入时标记不匹配的括号、缺失逗号、错误缩进和未定义名称,因此大多数错误永远不会到达解释器。一次测试少量代码也能隔离任何漏网的错误。
Python 中 print 是函数还是语句?
在 Python 3 中它是函数,因此你必须用括号调用它:print("text")。裸露的 print "text" 形式仅在 Python 2 中有效,现在是语法错误。如果你看到"Missing parentheses in call to print",说明你在 Python 3 解释器中运行 Python 2 风格的代码。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
