2012-08-16 17 views
18

Supongamos que tengo una línea múltiple en Python que genera una excepción.Qué línea se elige para informar en la excepción

¿Cómo decide Python con qué línea oponer la excepción?

Ejemplos: (Nota: Podría haber usado barras invertidas \ después de cada línea)

(1 
+0/0 
+3) 

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

(1 
+ 
0/0 
) 

produce una excepción en línea 3.

(0/0 
+ 
1) 

produce una excepción en línea 2.

Esta pregunta fue inspirada por this example, y @Godman pointed out que las excepciones no ocurren solo en la última línea (como había pensado anteriormente).

+0

Ya sabes, me preguntaba lo mismo cuando comencé la otra pregunta. –

+0

En ese caso, podría haber sido otra razón muy localizada (por ejemplo, podría haber una diferencia entre el archivo .py y el archivo .pyc). –

+7

Por lo general, es la última línea. En caso de que el archivo de origen y el código en ejecución realmente no estén sincronizados, podría ser cualquier línea. Puede usar 'dis.dis()' para ver los números de línea asociados con cada instrucción bytecode. –

Respuesta

1

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

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

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

.

Sin embargo, si esto no es el comportamiento que se ve archivos (fuente) y, o bien su correspondiente (compilado) Archivo PYC, o el código que se ejecuta, se puede ser causada por discrepancias en uno de sus py (en memoria). El siguiente es un ejemplo ilustrativo.

  • Supongamos E.py contiene:

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

  • llamada E.z(), que producirá una excepción, en la línea 2 en z, mostrando la línea 0/0 - ninguna sorpresa aquí.

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

  • Regrese en la línea de comando de python y llame al E.z() 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 comentar uno si te encuentras con uno. ¡Había pensado previamente que era simplemente la última línea!

+0

No importa si los archivos '.py' y' .pyc' están fuera de sincronización o no. Lo importante es si el archivo '.py' está sincronizado con el código actual (que está en la memoria). –

+0

Gracias @SvenMarnach, no me di cuenta de que esto estaba en la memoria. –

4

Fundamentalmente, no creo que todos estemos pensando en la línea correcta. No hay tal cosa como la última línea aquí. La excepción la plantea el intérprete 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 tocas un corchete de cierre ')'. Una breve explicación de lo mismo fue dada por Joran Beasley en los comentarios en contra de la pregunta en sí.

Puede hacer 3 cosas hacen juzgar la exactitud de esta, sin ahondar más profundamente en la gramática: -

  1. escribir este código en el intérprete de Python:

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

Esto también plantea ZeroDivionError.

  1. escribir este código en el intérprete de Python:

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

Este le da una sintaxis inválida ya que la expresión no está completa y no puede ser analizada por el intérprete PD: Este es el mismo que el código mencionado en la pregunta

  1. escribir este código en el intérprete Python:

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

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

PD: Perdone el formato de la respuesta.

Nueva edición: -

@ Hayden: pensé que sería fácil de explicar las sutilezas por no ahondar demasiado profundamente 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 a seguir: - 1. Escriba el código que se le pide en la pregunta en un archivo temp.py y guárdelo, luego, importe la temperatura 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 en byteCodeDetails.py y ejecute el archivo en el símbolo del sistema como: python byteCodeDetails.py temp.pyc. El show_file función será llamada aquí y dará el siguiente resultado: -

03f30d0a magia
moddate 458c2e50 (Vie Ago 17 de 2012 23:54:05) Código
argcount 0 0
nlocals stacksize 3 banderas código 0040
640600640200640200151764030017640400175a000064050053 5
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
Ninguno
nombres ('A',)
nombres de variables()
freevars()()
cellvars
nombre de archivo 'C: \ Users \ Python \ temp1.py'

nombre de ''
firstlineno 5
lnotab


lo tanto, como se puede observar que: -

  1. Citando desde el enlace mencionado anteriormente: En la salida desmontada, 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 siguientes números (0, 3, 6, 9 , ...) son los offsets de bytes de la instrucción. De manera similar, para su código, el número más a la izquierda es solo 5, que es el número de línea, y las columnas a la derecha representan los mnemónicos (para ser leídos por el intérprete) traducidos por el compilador para su código. se forman expresiones y su formación es superada por el valor de los números de línea en el código compilado.
  2. firstlineno señala a 5.

Ahora, basta con 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: -

03f30d0a magia
moddate 0f8e2e50 (Sab Ago 18 de 2012 00:01:43)
código
argcount 0 0
nlocals
stacksize 3

banderas 0040
código 640600640200640200151764030017640400175a000064050053 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
Ninguno
nombres ('a',)
nombres de variables()
freevars()
cellvars()
nombre de archivo 'C: \ Usuarios \ Python \ temp1.py'
nombre de ''
firstlineno 4

lnotab 0F01

Pues bien, ahora y ou puede ver claramente 2 cosas: -

  1. El código byte se compone de 2 líneas que se muestran en la siguiente línea a 'código 640600640200640200151764030017640400175a000064050053', con el prefijo '4' y '5'. Esto muestra que el compilador ha analizado el archivo .py y convertido el código en temp.py en 2 líneas de código que serán ejecutadas por el intérprete.Tenga en cuenta que aquí el contenido de la línea 4 serán ejecutadas por el intérprete no importa la expresión es completa o no
  2. El valor de cambios firstlineno a 4 en lugar de 5

El punto de esta larga discusión es que siempre que el código de bytes indica al intérprete que aquí es donde comienza una línea y las declaraciones correspondientes que se deben ejecutar para esta línea, entonces, el intérprete simplemente ejecuta esa línea y las declaraciones correspondientes escritas a su lado.

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

+0

Tienes razón, en cierto sentido, la "línea" puede permanecer intacta entre paréntesis. No creo que esto cambie nada "fundamentalmente", pero disfruté el juego de palabras. :) –

+0

@ Hayden: No estoy seguro de que estés de acuerdo con la respuesta o no – GodMan

+0

No estoy de acuerdo, ya que ** hay ** un concepto de último línea (de una expresión) incluso en el caso que le preocupa: * Si la expresión está escrita usando paréntesis, la "última línea" es simplemente la que contiene el ')' final. * ** Parece que los errores de sintaxis ** mostrar la fina l línea como otras excepciones ... –

Cuestiones relacionadas