Почему python печатает только одно сообщение об ошибке?

Многие известные мне языки (например, C++, C, Rust и т. д.) выводят несколько сообщений об ошибках одновременно. Тогда почему python печатает только одно сообщение об ошибке?


person xilpex    schedule 14.05.2020    source источник
comment
Знаете ли вы разницу между компилируемым и интерпретируемым языками?   -  person Algirdas Preidžius    schedule 15.05.2020
comment
@AlgirdasPreidžius -- Да.   -  person xilpex    schedule 15.05.2020
comment
@MattDMo - Да, вы можете получить некоторые сложные трассировки, но не множественные ошибки, такие как синтаксические или семантические ошибки.   -  person xilpex    schedule 15.05.2020
comment
Вот тут-то и возникает интерпретация против компиляции. В скомпилированной программе синтаксические ошибки и т. д. обнаруживаются во время компиляции, а не во время выполнения. В интерпретируемой среде время компиляции и выполнения происходит вместе (по сути).   -  person MattDMo    schedule 15.05.2020
comment
@MattDMo Я думаю, он имеет в виду, что вы просто получаете сообщение о первой обнаруженной ошибке. В отличие от компилятора C, который сообщает обо всех ошибках, обнаруженных в программе.   -  person Barmar    schedule 15.05.2020
comment
@MattDMo Но Python не интерпретируется строго. Сначала он скомпилирован в байтовый код, и он мог бы сообщать обо всех найденных синтаксических ошибках, но этого не происходит.   -  person Barmar    schedule 15.05.2020


Ответы (3)


Во-первых, я предполагаю, что мы говорим о синтаксических ошибках, то есть о тех, которые могут (и должны) обнаруживаться и сообщаться компилятором.

В первую очередь это выбор дизайна. Python в основном построен на представлении о том, что все должно делаться во время выполнения. И компилятор намеренно сделан максимально простым.

Простой и легкий для понимания или сложный и изощренный. Проще говоря, у вас есть выбор между использованием очень простого компилятора, который легко понять и поддерживать, или сложным механизмом со сложной анализ и оптимизация программы.

Такие языки, как C, C++ и Rust черпают свою силу в сильной оптимизации кода во время компиляции и, таким образом, выбрали второй путь с очень сложными и чрезвычайно изощренными компиляторами. Работа с синтаксическими ошибками — скорее одно из их менее впечатляющих достижений.

Python, с другой стороны, пошел другим путем. В самом деле, компилятору Python совершенно невозможно предсказать, что именно делает фрагмент кода Python, не запустив его на самом деле, что в первую очередь исключает все интересные возможности для оптимизации, и, следовательно, сложный компилятор на самом деле не имеет смысла. , тем не мение. Таким образом, сохранение простоты компилятора Python и сосредоточение внимания на оптимизации времени выполнения является правильным выбором. Но у этого есть и обратная сторона: компилятор просто отключается всякий раз, когда обнаруживает ошибку.


Чтобы дать немного больше контекста...

1. Исправление ошибок

Работа с ошибками и восстановление после синтаксической ошибки в компиляторе — сложная задача.

Компиляторы обычно очень хорошо умеют быстро и эффективно переводить (синтаксически) правильные программы в машинный код, представляющий исходную программу. Однако при наличии синтаксической ошибки компилятор часто не может угадать первоначальное намерение программиста, и поэтому неясно, что делать с ошибочным фрагментом кода.

Вот очень простой пример:

pen color("red")

Очевидно, здесь что-то не так, но без дальнейшего контекста невозможно сказать, было ли первоначальное намерение этой строки pen = color("red"), pencolor("red"), pen.color("red") или что-то совсем другое.

Если компилятор хочет продолжить просмотр остальной части программы (и, таким образом, обнаружить потенциально больше синтаксических ошибок), ему нужна стратегия того, как справляться с такими ситуациями и восстанавливаться, чтобы двигаться дальше: ему нужна ошибка стратегия восстановления. Это может быть что-то столь же простое, как пропуск всей строки или отдельных токенов, но для этого нет четкого «правильного» решения.

2. Компилятор Python

Python компилирует вашу программу по одному символу за раз.

Текущий компилятор Python работает, просматривая один символ за раз (называемый компилятором LL(1)). Это делает автоматическую сборку компилятора для Python чрезвычайно простой и довольно быстрой и эффективной. Но это означает, что бывают ситуации, когда, несмотря на «очевидную» синтаксическую ошибку, Python благополучно продолжает компилировать программу до тех пор, пока она действительно не потеряется.

Взгляните на этот пример:

x = foo(
y = bar()
if x > y:

Как люди, мы быстро видим отсутствие закрывающей скобки в строке 1. Однако с точки зрения компилятора это выглядит скорее как вызов с именованным аргументом, примерно так:

x = foo(y = bar() if x > y else 0)

Соответственно, Python заметит, что что-то не так, только когда наткнется на двоеточие в строке 3 — первый символ, который не работает с его «предположением». Но в этот момент крайне сложно понять, что делать с этим куском кода и как правильно восстановить: в этом случае вы просто пропускаете двоеточие? Или вы должны вернуться и исправить что-то ранее, и если да, то как далеко вы собираетесь вернуться?

3. Последующие ошибки

Исправление ошибок может привести к возникновению "фантомных" ошибок.

В первом приведенном выше примере компилятор мог просто пропустить всю строку и двигаться дальше без каких-либо проблем. Но бывают ситуации, когда выбор способа восстановления после синтаксических ошибок влияет (потенциально) на все последующее, как в этом примере:

deffoo(x):

Намерение, стоящее за этим, может быть либо def foo(x):, либо просто вызовом deffoo(x). Но это различие определяет, как компилятор будет смотреть на следующий код, и либо сообщит об ошибке отступа, либо, возможно, return вне функции и т. д.

Опасность исправления ошибок заключается в том, что догадка компилятора может быть на самом деле ошибочной, что может привести к целому ряду сообщений о последующих ошибках, которые могут быть даже не настоящими ошибками, а скорее призраками, созданными неправильным решением компилятора.


Итог: добиться правильного восстановления после ошибок и составления отчетов об ошибках чрезвычайно сложно. Таким образом, выбор Python сообщать только о первой синтаксической ошибке, с которой он сталкивается, является разумным и прекрасно работает для большинства пользователей и ситуаций.

На самом деле я написал анализатор с более сложным обнаружением ошибок, который может перечислить все ошибки, которые он обнаруживает в программе Python. Но, по моему опыту, слишком много дополнительных ошибок, помимо первой, — это просто мусор, и поэтому я всегда придерживался отображения только первой ошибки в программе.

person Tobias    schedule 16.05.2020
comment
Вау, отличный ответ, хотя я не вижу другого парсера, кроме LL(1), такого как L(AL)R(1) или GLR, который мог бы работать лучше. Хотя я думаю, что рекурсивный спуск отлично подойдет :-) - person xilpex; 16.05.2020
comment
@Xilpex Спасибо. На самом деле, вы можете улучшить ситуацию, заглянув вперед, а не переключаясь, скажем, на LR. Если вы используете что-то вроде (A)LL(*) (например, ANTLR), вы можете получить лучшие результаты. - person Tobias; 16.05.2020
comment
Ах, да -- кажется, я забыл об этом/ - person xilpex; 16.05.2020

Затрудняюсь ответить точно почему. Я не могу заглянуть в голову разработчикам 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 и другие, могут быть настроены на автоматический запуск этих инструментов и сообщать о любых проблемах еще до того, как вы выполните свой код.

person gelonida    schedule 14.05.2020

C или C++ используют компилятор для компиляции и последующего выполнения, но python является интерпретируемым языком, это означает, что интерпретатор читает каждую строку, а затем выполняет ее, когда интерпретатор видит ошибку в строке программы, которую он остановил, и отображает ошибку на другом интерпретируемом языке, например JS это то же самое. Я надеюсь, что ваша проблема решена, но если вы хотите узнать больше, вы можете погуглить «Интерпретируемые и скомпилированные языки» или посмотреть это

person mmnsh    schedule 15.05.2020
comment
Это неправильно. Python не интерпретируется строго. Сначала он переходит к байт-коду перед его запуском. - person xilpex; 15.05.2020