为什么 python 只打印一条错误消息?
Why does python print only one error message?
我知道的很多语言(如 C++、C、Rust 等)都会一次打印多条错误消息。那为什么python只打印一条错误信息呢?
很难回答确切原因。我无法查看 C-python、jython、pypy 或其他开发人员的头部。
许多错误只会在 运行 时出现,因为 python 是一种没有严格类型化的解释型语言。
但是如果没有语法错误,每个文件都会被编译成字节码。
所以对于语法错误我不能给你原因,因为这在技术上应该是可行的。但是我从来没有遇到过这个问题,因为我使用像 pylint
或 flake8
这样的工具来为我检查代码。
这些工具可以检测多个错误,并给出很多关于编码风格的警告。
所以我不能告诉你原因,只能告诉你怎么做才能一次出现多个错误。
这些工具可以配置为只显示某些类型的错误。
要安装一个或另一个工具,只需键入:
pip install flake8
或 pip install pylint
然后在所有代码所在的目录中键入 flake8
或 pylint
或键入 flake8 <filename>
或 pylint <filename>
以仅检查一个文件。
请注意,许多 IDE,例如 Microsoft Visual Studio Code、Pycharm 和其他 IDE 都可以配置为 运行 这些工具自动为您服务,并在出现任何问题之前发出信号你执行你的代码。
C 或 C++ 是使用编译器编译然后执行,但 python 是一种解释型语言,这意味着解释器读取每一行,然后在解释器在它停止的程序行中看到错误时执行它和用 JS 等其他解释型语言显示错误是一样的。
我希望你的问题已解决,但如果你想阅读更多内容,可以 google "Interpreted and compiled languages" 或查看 this
首先,我假设我们正在谈论语法错误,即那些可以(并且应该)被编译器检测和报告的错误。
这主要是一种设计选择。 Python 基本上是建立在一切都应该在运行时完成的概念之上的。并且编译器有意保持尽可能简单。
简单易懂,复杂精巧:
简而言之,您可以选择使用易于理解和维护的非常简单的编译器,或者使用复杂的机器进行复杂的程序分析和优化。
像 C、C++ 和 Rust 这样的语言在编译过程中通过大量优化代码来发挥自己的优势,因此采用了高度复杂和极其复杂的编译器的第二条路线。处理语法错误是他们不那么令人印象深刻的壮举之一。
Python,另一方面,去了另一条路。实际上,一般来说,Python 编译器在没有实际 运行 的情况下预测一段 Python 代码的确切作用是完全不可能的——这排除了所有有趣的优化机会首先,因此复杂的编译器无论如何都没有真正意义。因此,保持 Python 的编译器简单并专注于运行时优化是正确的选择。但它的缺点是编译器在发现错误时会简单地退出。
提供更多背景信息...
1。错误恢复
在编译器中处理错误并从语法错误中恢复是很困难的。
编译器通常非常擅长将(语法上)正确的程序快速有效地翻译成代表原始程序的机器码。但是,如果出现语法错误,编译器往往无法猜出程序员的初衷,因此不清楚如何处理错误的代码.
这是一个非常简单的例子:
pen color("red")
显然,这里有问题,但如果没有进一步的上下文,就无法判断这行的本意是 pen = color("red")
、pencolor("red")
、pen.color("red")
还是什么否则完全。
如果编译器想继续查看程序的其余部分(从而发现更多潜在的语法错误),它需要一种策略来应对这种情况并恢复以便继续前进:它需要错误恢复策略。这可能就像跳过整行或单个标记一样简单,但对此没有明确的 "correct" 解决方案。
2。 Python 的编译器
Python 一次编译一个符号。
Python 的当前编译器通过一次查看一个符号来工作(称为 LL(1)
编译器)。这使得为 Python 自动构建编译器变得极其简单,而且非常快速和高效。但这意味着在某些情况下,尽管存在 "obvious" 语法错误,Python 仍会愉快地继续编译程序,直到它真正丢失。
看看这个例子:
x = foo(
y = bar()
if x > y:
作为人类,我们很快就会看到第 1 行中缺少的右括号。但是,从编译器的角度来看,这看起来更像是一个带有命名参数的调用,如下所示:
x = foo(y = bar() if x > y else 0)
因此,Python 只会在遇到第 3 行中的冒号时注意到有问题——第一个与其 "assumption" 不起作用的符号。但到那时,很难弄清楚如何处理这段代码,以及如何正确恢复:在这种情况下,你只是跳过冒号吗?或者你应该回去更早地纠正一些事情——如果是这样,你要回到多远?
3。后续错误
错误恢复会造成 "ghost" 错误。
在上面的第一个示例中,编译器可以跳过整行并继续前进而不会出现任何问题。但在某些情况下,如何从语法错误中恢复的选择会(可能)影响后面的一切,如本例所示:
deffoo(x):
这背后的意图可能是 def foo(x):
或者只是一个电话 deffoo(x)
。但是这种区别决定了编译器将如何查看后面的代码,并报告缩进错误,或者可能是函数外部的 return
等
错误恢复的危险在于编译器的猜测实际上可能是错误的,这可能导致报告的一系列后续错误——这些错误甚至可能不是真正的错误,而是由编译器的错误造成的幻影决定。
底线: 错误恢复和正确报告错误非常困难。 Python 选择仅报告它遇到的第一个语法错误是明智的,并且适用于大多数用户和情况。
我实际上写了一个parser with more sophisticated error detection,它可以列出它在Python程序中发现的所有错误。但根据我的经验,除了第一个错误之外,还有太多其他错误只是垃圾,因此我总是坚持只显示程序中的第一个错误。
我知道的很多语言(如 C++、C、Rust 等)都会一次打印多条错误消息。那为什么python只打印一条错误信息呢?
很难回答确切原因。我无法查看 C-python、jython、pypy 或其他开发人员的头部。
许多错误只会在 运行 时出现,因为 python 是一种没有严格类型化的解释型语言。
但是如果没有语法错误,每个文件都会被编译成字节码。
所以对于语法错误我不能给你原因,因为这在技术上应该是可行的。但是我从来没有遇到过这个问题,因为我使用像 pylint
或 flake8
这样的工具来为我检查代码。
这些工具可以检测多个错误,并给出很多关于编码风格的警告。
所以我不能告诉你原因,只能告诉你怎么做才能一次出现多个错误。
这些工具可以配置为只显示某些类型的错误。
要安装一个或另一个工具,只需键入:
pip install flake8
或 pip install pylint
然后在所有代码所在的目录中键入 flake8
或 pylint
或键入 flake8 <filename>
或 pylint <filename>
以仅检查一个文件。
请注意,许多 IDE,例如 Microsoft Visual Studio Code、Pycharm 和其他 IDE 都可以配置为 运行 这些工具自动为您服务,并在出现任何问题之前发出信号你执行你的代码。
C 或 C++ 是使用编译器编译然后执行,但 python 是一种解释型语言,这意味着解释器读取每一行,然后在解释器在它停止的程序行中看到错误时执行它和用 JS 等其他解释型语言显示错误是一样的。 我希望你的问题已解决,但如果你想阅读更多内容,可以 google "Interpreted and compiled languages" 或查看 this
首先,我假设我们正在谈论语法错误,即那些可以(并且应该)被编译器检测和报告的错误。
这主要是一种设计选择。 Python 基本上是建立在一切都应该在运行时完成的概念之上的。并且编译器有意保持尽可能简单。
简单易懂,复杂精巧: 简而言之,您可以选择使用易于理解和维护的非常简单的编译器,或者使用复杂的机器进行复杂的程序分析和优化。
像 C、C++ 和 Rust 这样的语言在编译过程中通过大量优化代码来发挥自己的优势,因此采用了高度复杂和极其复杂的编译器的第二条路线。处理语法错误是他们不那么令人印象深刻的壮举之一。
Python,另一方面,去了另一条路。实际上,一般来说,Python 编译器在没有实际 运行 的情况下预测一段 Python 代码的确切作用是完全不可能的——这排除了所有有趣的优化机会首先,因此复杂的编译器无论如何都没有真正意义。因此,保持 Python 的编译器简单并专注于运行时优化是正确的选择。但它的缺点是编译器在发现错误时会简单地退出。
提供更多背景信息...
1。错误恢复
在编译器中处理错误并从语法错误中恢复是很困难的。
编译器通常非常擅长将(语法上)正确的程序快速有效地翻译成代表原始程序的机器码。但是,如果出现语法错误,编译器往往无法猜出程序员的初衷,因此不清楚如何处理错误的代码.
这是一个非常简单的例子:
pen color("red")
显然,这里有问题,但如果没有进一步的上下文,就无法判断这行的本意是 pen = color("red")
、pencolor("red")
、pen.color("red")
还是什么否则完全。
如果编译器想继续查看程序的其余部分(从而发现更多潜在的语法错误),它需要一种策略来应对这种情况并恢复以便继续前进:它需要错误恢复策略。这可能就像跳过整行或单个标记一样简单,但对此没有明确的 "correct" 解决方案。
2。 Python 的编译器
Python 一次编译一个符号。
Python 的当前编译器通过一次查看一个符号来工作(称为 LL(1)
编译器)。这使得为 Python 自动构建编译器变得极其简单,而且非常快速和高效。但这意味着在某些情况下,尽管存在 "obvious" 语法错误,Python 仍会愉快地继续编译程序,直到它真正丢失。
看看这个例子:
x = foo(
y = bar()
if x > y:
作为人类,我们很快就会看到第 1 行中缺少的右括号。但是,从编译器的角度来看,这看起来更像是一个带有命名参数的调用,如下所示:
x = foo(y = bar() if x > y else 0)
因此,Python 只会在遇到第 3 行中的冒号时注意到有问题——第一个与其 "assumption" 不起作用的符号。但到那时,很难弄清楚如何处理这段代码,以及如何正确恢复:在这种情况下,你只是跳过冒号吗?或者你应该回去更早地纠正一些事情——如果是这样,你要回到多远?
3。后续错误
错误恢复会造成 "ghost" 错误。
在上面的第一个示例中,编译器可以跳过整行并继续前进而不会出现任何问题。但在某些情况下,如何从语法错误中恢复的选择会(可能)影响后面的一切,如本例所示:
deffoo(x):
这背后的意图可能是 def foo(x):
或者只是一个电话 deffoo(x)
。但是这种区别决定了编译器将如何查看后面的代码,并报告缩进错误,或者可能是函数外部的 return
等
错误恢复的危险在于编译器的猜测实际上可能是错误的,这可能导致报告的一系列后续错误——这些错误甚至可能不是真正的错误,而是由编译器的错误造成的幻影决定。
底线: 错误恢复和正确报告错误非常困难。 Python 选择仅报告它遇到的第一个语法错误是明智的,并且适用于大多数用户和情况。
我实际上写了一个parser with more sophisticated error detection,它可以列出它在Python程序中发现的所有错误。但根据我的经验,除了第一个错误之外,还有太多其他错误只是垃圾,因此我总是坚持只显示程序中的第一个错误。