¿Qué línea se elige para ser reportada en excepción

Supongamos que tengo una multilínea en Python que genera una excepción.

¿Cómo decide Python en qué línea se debe levantar la excepción?

Ejemplos: (Nota: podría haber usado barras diagonales tras cada línea)

 (1 +0/0 +3) 

Muestra una excepción en la línea 3 (una excepción ZeroDivisionError , en +3) ).

 (1 + 0/0 ) 

Lanza una excepción en la línea 3 .

 (0/0 + 1) 

Lanza una excepción en la línea 2 .

Esta pregunta se inspiró en este ejemplo , y @Godman señaló que las excepciones no solo ocurren en la última línea (como lo había pensado anteriormente).

Fundamentalmente, no creo que todos estemos pensando en la dirección correcta. No hay tal cosa como la última línea aquí. El intérprete lanza la excepción cuando recibe una expresión completa. De acuerdo con la gramática de Python: http://docs.python.org/reference/grammar.html , la expresión no está completamente completa hasta que golpeas un corchete de cierre ‘)’. Joran Beasley dio una breve explicación de lo mismo en los comentarios contra la pregunta en sí.

Puedes hacer 3 cosas para juzgar la exactitud de esto, sin profundizar mucho en la gramática:

  1. Escriba este código en el intérprete de python:

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

Esto también plantea ZeroDivionError.

  1. Escriba este código en el intérprete de python:

    a = (1 + 2 + 0/0 + 4 + 5 # Y, presione Entrar

Esto le da una syntax no válida ya que la expresión no está completa y no puede ser analizada por el intérprete PS: esto es lo mismo que el código mencionado en la pregunta

  1. Escriba este código en el intérprete de python:

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

Finalmente, la expresión no se completa hasta que golpeas la llave de cierre. Por lo tanto, puede continuar agregando más subexpresiones dentro de ella sin obtener ninguna excepción. Así, fundamentalmente, el intérprete no ve todo esto como números de línea; espera hasta que todas las expresiones (incluidas las subexpresiones) se hayan completado. Y, es un flujo de control de progtwigción adecuado para el intérprete.

PD: perdona mi formateo de la respuesta.

Nueva edición: –

@ Hayden: Pensé que sería fácil explicar las sutilezas al no profundizar demasiado en la gramática. Pero, para su referencia, solo estoy copiando el código de la URL: http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html

Pasos para ejecutar: – 1. Escriba su código preguntado en la pregunta en un archivo temp.py y guárdelo, luego, importe temp en otro archivo o en el intérprete. Esto creará temp.pyc 2. Ahora, copie y pegue el código completo en la URL mencionada anteriormente en byteCodeDetails.py y ejecute el archivo en el símbolo del sistema como: python byteCodeDetails.py temp.pyc. La función show_file se llamará aquí y dará el siguiente resultado:

magia 03f30d0a
moddate 458c2e50 (Vie 17 de agosto 23:54:05 2012) código
argcount 0
nlocals 0 stacksize 3 banderas código 0040
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 (Ninguno)
25 RETURN_VALUE
consts
1
2
0
4
5
Ninguna
3
nombres (‘a’,)
nombres de varillas ()
freevars ()
celdas celulares ()
nombre de archivo ‘C: \ Users \ Python \ temp1.py’

nombre ”
firstlineno 5
lnotab


Entonces, como pueden notar que:

  1. Citando el enlace mencionado anteriormente: en la salida desensamblada, los números más a la izquierda (1, 2, 3) son los números de línea en el archivo fuente original y los números siguientes (0, 3, 6, 9, …) Son las compensaciones en bytes de la instrucción. De manera similar, para su código, el número que está más a la izquierda es solo 5, que es el número de línea, y las columnas a la derecha representan las mnemotécnicas (para que las lea el intérprete) traducidas por el comstackdor para su código. se forman y su formación es superada por el valor de los números de línea en el código comstackdo.
  2. Firstlineno apunta a 5.

Ahora, solo haga un ligero cambio en su código inicial en el archivo temp.py:

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

Ahora, ejecute los 2 pasos anteriores una vez más. La siguiente es la salida:

magia 03f30d0a
moddate 0f8e2e50 (sáb 18 de agosto, 00:01:43 2012)
código
argcount 0
nlocals 0
stacksize 3

banderas 0040
código 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 (Ninguno)
25 RETURN_VALUE
consts
1
2
0
4
5
Ninguna
3
nombres (‘a’,)
nombres de varillas ()
freevars ()
celdas celulares ()
nombre de archivo ‘C: \ Users \ Python \ temp1.py’
nombre ”
firstlineno 4

lnotab 0f01

Bueno, ahora puedes ver claramente 2 cosas: –

  1. El código de bytes se compone de 2 líneas que se muestran en la línea siguiente al ‘código 640600640200640200151764030017640400175a000064050053’, con el prefijo ‘4’ y ‘5’. Esto muestra que el comstackdor ha analizado el archivo .py y ha convertido el código en temp.py en 2 líneas de código que ejecutará el intérprete. Tenga en cuenta que aquí el intérprete ejecutará los contenidos de la línea 4 independientemente de que la expresión esté completa o no.
  2. El valor de firstlineno cambia a 4 en lugar de 5

El punto central de esta larga discusión es que siempre que el código de byte indique al intérprete que es donde comienza una línea y las instrucciones correspondientes que deben ejecutarse para esta línea, entonces, el intérprete simplemente ejecuta esa línea y las declaraciones correspondientes escritas a continuación. lo.

El código en su pregunta muestra firstlineno como 5, es por eso que recibe un error en la línea 5. Esperemos que esto ayude ahora.

La excepción apuntará a la línea * que contiene :

  1. El último operador (si los literales / operadores anteriores causaron la excepción).

  2. El último literal (de lo contrario, es decir, el último literal / operador causó la excepción).

.

Sin embargo , si este no es el comportamiento que ve, puede deberse a discrepancias en uno de sus archivos py (fuente) y su archivo pyc correspondiente (comstackdo), o el código en ejecución (en la memoria). El siguiente es un ejemplo ilustrativo.

  • Supongamos que E.py contiene:

     def z(): 0/0 
  • Desde la línea de comandos de python, import E (esto comstackrá E.py en el código de byte: E.pyc y lo E.pyc en la memoria).

  • Llame a Ez() , que producirá una excepción, en la línea 2 en z , mostrando la línea 0/0 , no es de extrañar aquí.

  • Regrese al archivo fuente E.py , inserte dos líneas en la parte superior, y en el segundo inserte la cadena "oh dear, oh dear" .

  • Vuelva a la línea de comandos de python y llame a Ez() por segunda vez.

  • La excepción (en la línea 2, en z ) ahora muestra "oh dear, oh dear" .

* Actualización : no tengo una referencia para esto, por favor comenta una si te encuentras con una. ¡Antes había pensado que era simplemente la última línea!