Los errores de sintaxis se señalan con un mensaje que aparece en la línea de código en la cual se ha detectado el error, además de una pequeña flecha que indica el elemento siguiente al punto en que se ha detectado. En el ejemplo anterior, el error ha sido detectado antes de print(i) y, de hecho, inmediatamente después de range(10) faltan los dos puntos necesarios para terminar la instrucción for. También se muestran el nombre del archivo y el número de línea para así facilitar la identificación del error:
Los errores de sintaxis se señalan antes de que el programa sea ejecutado, puesto que se generan durante la fase de compilación en bytecode:
Las excepciones
Las excepciones se generan cuando una instrucción, aunque sintácticamente correcta, da lugar a errores durante su ejecución. Consideremos, por ejemplo, el archivo errors.py:
Hay un error tipográfico (hemos escrito prin en lugar de print), el cual será identificado solo cuando el flujo de ejecución llegue a esta instrucción, puesto que solo en dicho momento Python buscará la definición de la etiqueta prin. Así, el error se generará únicamente si el usuario escribe el carácter a:
Como se puede ver en este ejemplo, cuando las excepciones no son generadas por el programa, la ejecución termina y se muestra un mensaje de error. Este mensaje se distingue de los derivados de errores de sintaxis principalmente por la presencia del traceback.
NOTA
Las exepciones también pueden mostrarse a propósito, mediante la instrucción raise. Consideremos, por ejemplo, el archivo siguiente:
Esto es lo que ocurre cuando lo ejecutamos:
Hablaremos con mayor profundidad de la instrucción raise en el Capítulo 5.
Puesto que las excepciones son generadas solo cuando el fragmento de código que contiene el error es ejecutado, es posible que un programa funcione casi siempre. Si hubiéramos ejecutado el archivo errors.py efectuando 100 bucles, sin escribir nunca el carácter a, posiblemente hubiéramos estado seguros de que el programa no contenía error alguno. En el caso de que ocurran este tipo de problemas, nos podría ser útil utilizar alguna herramienta que analice el código Python con el fin de detectar posibles errores. Entre estos, citamos pyflakes y pylint. He aquí un ejemplo de uso de este último:
La gestión de las excepciones
En esta sección presentamos el mecanismo de gestión de las excepciones. Para ello, consideremos el archivo myfile.py:
Si lo ejecutamos y escribimos una cadena de texto que no puede ser convertida a entero, la función integrada int() detecta una excepción de tipo ValueError:
Podemos gestionar la excepción insertando entre las palabras clave try y except la línea lógica que da lugar al error. Este se gestionará en el bloque de código que sigue a la palabra clave except:
La palabra clave try es una instrucción compuesta y except es una cláusula única. Cuando son ejecutadas las instrucciones de la suite del try, si no surge ningún error, la ejecución pasa directamente de la última instrucción del bloque try a la instrucción siguiente a la try/except, omitiendo por tanto la suite de la except. En cambio, si una línea lógica en la suite del try detecta una excepción, la ejecución desde dicha línea pasa directamente a la cláusula except. Esta comprueba que el tipo de la excepción detectada corresponda al que se trata de gestionar, en cuyo caso se ejecuta su suite para poder gestionar el error; en caso contrario, la suite except se omite.
En nuestro caso, la except gestiona solo las excepciones de tipo ValueError. Por tanto, si escribimos un número que genera un IndexError cuando se intenta indexar la lista, esta excepción no será gestionada:
Si se desean gestionar los dos tipos de excepciones en la misma cláusula except, es posible insertar los tipos ValueError e IndexError en una tupla, del modo siguiente:
También es posible separar la gestión de los distintos tipos de excepción en diferentes cláusulas except:
Acabamos esta sección diciendo que, si en la cláusula except no se especifica ningún tipo de excepción, se capturarán las excepciones de todos los tipos:
No debemos utilizar esta modalidad solo por pereza, porque, si lo que hace nuestro código no está absolutamente claro, hay muchas probabilidades de que perdamos el tiempo en vez de ganarlo. Y esto porque en la except no se muestra ningún mensaje de error, por lo que, si se comprobaran los errores que no tenemos previstos, no conseguiríamos entender los motivos del mal funcionamiento. Además, estos errores imprevistos serían gestionados del mismo modo que aquellos que sí hemos previsto gestionar en la except, aunque quizás habrían debido ser gestionados de manera separada. Esto en el mejor de los casos, porque, por la ley de Murphy, nuestra pereza normalmente será la causa de errores lógicos difíciles de detectar y localizar.
En el Capítulo 5 hablaremos en detalle de las excepciones y, entre otras cosas, veremos como el mecanismo de la herencia nos permite capturar de manera apropiada todas las excepciones, dejando que se propaguen aquellas que no representan errores.
Objetos iterables, iteradores y contexto de iteración
Los argumentos tratados en esta sección pueden no resultar del todo claros, por lo que si lo encontramos difícil, no nos desesperemos: podremos regresar a dichos argumentos en otra ocasión, una vez hayamos adquirido un poco más de soltura con el lenguaje.
El protocolo de iteración
El protocolo de iteración se describe en la PEP-0234.