Подтвердить что ты не робот

Какую строку выбрать для исключения в исключении

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

Как Python решает, какая строка поднимает исключение?

Примеры: (Примечание: я мог бы использовать обратную косую черту \ после каждой строки)

(1
 +0/0
 +3)

Оставляет исключение в строке 3 (исключение ZeroDivisionError, при +3)).

(1
 +
 0/0
 )

Выдает исключение в строке 3.

(0/0
 +
 1)

Выдает исключение в строке линии 2.

Этот вопрос был вдохновлен этим примером, а @Godman указал, что исключения не просто происходят на последней строке (как я думал ранее).

4b9b3361

Ответ 1

В принципе, я не думаю, что все мы думаем о правильных строках. Здесь нет такой вещи, как последняя строка. Исключение возникает интерпретатором, когда он получает выражение полностью. Согласно грамматике Python: http://docs.python.org/reference/grammar.html, выражение не будет полностью заполнено, пока вы не нажмете закрывающую фигуру ")". Краткое объяснение тому же было дано Джораном Бисли в комментариях к самому вопросу.

Вы можете сделать 3 вещи, чтобы судить о правильности этого, не углубляясь глубоко в грамматику: -

  • Введите этот код в интерпретаторе python:

    а = (1 + 2 + 0/0 + 4 + 5)

Это также вызывает ZeroDivionError.

  • Введите этот код в интерпретаторе python:

    a = (1 + 2 + 0/0 + 4 + 5 # И нажмите enter

Это дает неверный синтаксис, поскольку выражение не является полным и не может быть проанализировано интерпретатором PS: Это то же самое, что и код, упомянутый в вопросе

  • Введите этот код в интерпретаторе python:

a = (1
    +2
    +0/0
    +4
    +5)

В конце концов, выражение не будет завершено, пока вы не нажмете на закрывающую фигуру. Следовательно, вы можете продолжать добавлять в него дополнительные подвыражения, не получая никаких исключений. Таким образом, в основном интерпретатор не видит все это как номера строк; он ждет, пока все выражения (включая подвыражения) не будут завершены. И, это правильный поток управления программированием для интерпретатора.

PS: Прошу форматировать ответ.

Новое редактирование: -

@Хайден: Я думал, что будет легко объяснить тонкости, не углубляясь слишком глубоко в грамматику. Но для вашей справки я просто копирую код из URL-адреса: http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html

Шаги для запуска: - 1. Напишите свой код, заданный в вопросе, в файле temp.py и сохраните его, затем импортируйте temp в другом файле или в интерпретаторе. Это создаст temp.pyc 2. Теперь скопируйте и вставьте полный код в указанном выше URL-адресе в файле byteCodeDetails.py и запустите файл в командной строке как: python byteCodeDetails.py temp.pyc. Здесь будет вызываться функция show_file и будет выдавать следующий результат: -

магия 03f30d0a
moddate 458c2e50 (пт авг. 17 23:54:05 2012) код
argcount 0
nlocals 0 stacksize 3 flags 0040 code
640600640200640200151764030017640400175a000064050053 5
0
LOAD_CONST 6 (3)
              3 LOAD_CONST 2 (0)
              6 LOAD_CONST 2 (0)
              9 BINARY_DIVIDE
             10 BINARY_ADD
             11 LOAD_CONST 3 (4)
             14 BINARY_ADD
             15 LOAD_CONST 4 (5)
             18 BINARY_ADD
             19 STORE_NAME 0 (a)
             22 LOAD_CONST 5 (Нет)
             25 RETURN_VALUE
consts
       1
       2
       0
       4
       5
       None
       3
имена ('a',)
varnames()
freevars()
cellvars()
filename 'C:\Users\Python\temp1.py'

имя ''
firstlineno 5
lnotab


Итак, как вы можете заметить, что: -

  • Цитата из ссылки, упомянутой выше: В разобранном выходе самые левые числа (1, 2, 3) являются номерами строк в исходном исходном файле, а следующие числа (0, 3, 6, 9,...) являются байтовыми смещениями команды, Аналогично, для вашего кода самое левое число - это только 5, которое является номером строки, а столбцы справа представляют мнемонику (для чтения интерпретатором), переведенную компилятором для вашего кода. Таким образом, указывается, как выражения формируются, и их формирование превосходит значение номеров строк в скомпилированном коде.
  • firstlineno указывает на 5.

Теперь сделайте небольшое изменение в исходном коде в файле temp.py: -

a = (1
    +2
    +0/0
    +4 +
    5)

Теперь запустите еще два шага. Ниже приведен вывод: -

магия 03f30d0a
moddate 0f8e2e50 (сб авг 18 00:01:43 2012)
код
argcount 0
nlocals 0
stacksize 3

флаги 0040
код 640600640200640200151764030017640400175a000064050053
4
0 LOAD_CONST 6 (3)
              3 LOAD_CONST 2 (0)
              6 LOAD_CONST 2 (0)
              9 BINARY_DIVIDE
             10 BINARY_ADD
             11 LOAD_CONST 3 (4)
             14 BINARY_ADD

5 15 LOAD_CONST 4 (5)
             18 BINARY_ADD
             19 STORE_NAME 0 (a)
             22 LOAD_CONST 5 (Нет)
             25 RETURN_VALUE
consts
      1
      2
      0
      4
      5
      None
      3
names ('a',)
varnames()
freevars()
cellvars()
filename 'C:\Users\Python\temp1.py'
имя ''
firstlineno 4

lnotab 0f01

Хорошо, теперь вы можете ясно видеть 2 вещи: -

  • Байт-код состоит из двух строк, показанных в следующей строке, на "код 640600640200640200151764030017640400175a000064050053" с префиксом "4" и "5". Это показывает, что компилятор проанализировал файл .py и преобразовал код в temp.py в 2 строки кода, которые будут выполняться интерпретатором. Обратите внимание, что содержимое строки 4 будет выполняться интерпретатором независимо от того, будет ли это выражение завершено или нет
  • Значение firstlineno изменяется на 4 вместо 5

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

Код в вашем вопросе показывает firstlineno как 5, поэтому вы получаете сообщение об ошибке в строке 5. Надеюсь, это поможет сейчас.

Ответ 2

Исключение будет указывать на строку *, содержащую:

  • Последний оператор (если предыдущие литералы/операторы вызвали исключение).

  • Последний литерал (в противном случае последний оператор literal/вызвал исключение).

.

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

  • Предположим, что E.py содержит:

    def z():
        0/0
    
  • Из командной строки python import E (это скомпилирует E.py в байтовый код: E.pyc и помещает в память).

  • Вызов E.z(), который будет генерировать исключение, в строке 2 в z, отображая строку 0/0 - здесь не удивительно.

  • Вернитесь к исходному файлу E.py, вставьте две строки вверху, а во второй вставьте строку "oh dear, oh dear".

  • Вернитесь в командную строку python и вызовите E.z() второй раз.

  • Исключение (в строке 2, в z) теперь отображается "oh dear, oh dear".

* Обновление. У меня нет ссылки для этого, прокомментируйте его, если вы встретите его. Раньше я думал, что это просто последняя строка!