# Estructura del lenguaje Aunque ya sabes usar python de forma sencilla, aún no hemos tratado el comportamiento del lenguaje y cómo se estructura su sintaxis más allá de varios ejemplos sencillos y planos. En este apartado trataremos la estructura y las diferentes formas de controlar el flujo del programa. Como en la mayor parte de lenguajes de programación conocidos, python ejecuta las órdenes de arriba a abajo, línea por línea. Para demostrarlo, prueba a abrir un nuevo fichero, llenarlo con este contenido y ejecutarlo (`F5`). ``` python print("Esta línea va primero") print("Esta línea va segundo") print("Esta línea va tercero") print("Esta línea va cuarto") ``` Verás que el resultado del programa es siempre el mismo para todas las veces que lo ejecutes y siempre salen los resultados en el mismo orden. Para poder alterar el orden de los comandos, o elegir en función de una condición cuales se ejecutan, python dispone de unas estructuras. Pero, antes de contarte cuales son, te adelanto su estructura. Normalmente se declaran en una línea terminada en `:` y su cuerpo se sangra hacia dentro. La sangría o (indentación si lo calcamos del inglés[^indent]) es lo que define dónde empieza o termina un *bloque* en python. Las líneas consecutivas sangradas al mismo nivel se consideran el mismo *bloque*. #### Bloques Los *bloques* de código son conjuntos de órdenes que pertenecen al mismo contexto. Sirven para delimitar zonas del programa, cuerpos de sentencias, etc. - Puedes comenzar nuevos bloques incrementando el nivel de sangría. - Los bloques pueden contener otros bloques. - Los bloques terminan cuando el sangrado disminuye. Es **muy importante** sangrar los bloques correctamente, usando una sangría coherente. Puedes usar dos espacios, el tabulador, cuatro espacios o lo que desees, pero elijas lo que elijas debe ser coherente en todo el documento. IDLE y los editores de código en general puede configurarse para usar una anchura de indentación concreta, que se insertará cuando pulses la tecla tabulador. El estándar de python es cuatro espacios. [^indent]: ## Sintaxis general La sintaxis de python es sencilla en su concepción pero ha ido complicándose a medida que el lenguaje ha ido creciendo. En este apartado únicamente se mencionan los puntos más comunes de la sintaxis, dejando para futuros apartados los detalles específicos de conceptos que aún no se han explicado. ### Comentarios Los comentarios son *fundamentales* en el código fuente. El intérprete los ignora pero son primordiales para explicar detalles de nuestro código a otros programadores o a nosotros mismos en el futuro. Comentar bien el código fuente es un arte en sí mismo. Los comentarios en python se introducen con el símbolo `#`. Desde su aparición hasta el final de la línea se considera un comentario y python lo descarta. Pueden iniciarse a mitad de línea o en el inicio, tal y como se muestra a continuación: ``` python # En este ejemplo se muestran comentarios, como este mismo print("Hola") # Esto es otro comentario ``` ### Control de flujo Como ya se ha adelantado, es posible cambiar el orden de ejecución del programa en función de unas normas o evitar que el programa ejecute ciertas sentencias. A esto se le conoce como *control de flujo*. #### Condicionales Las condicionales son herramientas de *control de flujo* que permiten separar la ejecución en diferentes ramas en función de unas condiciones. En python sólo existe el condicional `if` (si, en castellano), aunque existen otras estructuras para conseguir el mismo efecto, no las trataremos aún. Ésta es la sintaxis del `if`: ``` python if condición: # Este bloque se ejecuta si la condición se cumple elif condiciónN: # Este bloque (opcional) se ejecuta si las condiciones previas no se # cumplen y la condiciónN sí se cumple else: # Este bloque (opcional) se ejecuta si todas las condiciones previas no # se cumplen ``` Tal y como se muestra en el ejemplo, los bloques `elif` y el bloque `else` son opcionales. Es posible, y muy común además, hacer un `if` únicamente con el apartado inicial. Si te preguntas qué condiciones debes usar, es tan simple como usar sentencias de python cuyo resultado sea `True` o `False`. Cuando la sentencia resulte en un `True` la condición se cumplirá y el bloque interior se ejecutará. En resumen, el *if* ejecuta el bloque *si* la condición se cumple. #### Bucles Existen dos tipos de sentencia para hacer repeticiones en python, ambas son similares al `if`, pero en lugar de elegir si una pieza de código se ejecuta o no, lo que deciden es si es necesario repetirla en función de una condición. ##### While El `while` (mientras que) es la más sencilla de estas estructuras, y la menos usada. ``` python while condición: # Este bloque se ejecutará siempre que la condición se considere verdadera ``` El `while` comprueba la condición en primer lugar, si resulta en `True` ejecuta el bloque interno y vuelve a comprobar la condición. Si es `True`, ejecuta el bloque de nuevo, y así sucesivamente. Es decir, ejecuta el bloque *mientras que* la condición se cumple. ##### For Los bucles *for* (para) son los más complejos y más usados, ``` python for preparación: # Bloque a repetir si la preparación funciona con el contexto creado por la # preparación else: # Bloque (opcional) a ejecutar si el bloque cuerpo no termina de forma # abrupta con un `break` ``` No te preocupes ahora mismo por el `else`, ya que se suele considerar python avanzado y no suele usarse. Más adelante en este capítulo analizaremos un ejemplo. En lo que a la preparación se refiere, el `for` es relativamente peculiar, sirve para ejecutar el primer bloque para un contexto concreto, el creado por una sentencia de preparación. Si la preparación falla el bucle se rompe. El uso más común del `for` es el de iterar en secuencias gracias al operador `in` que mencionamos anteriormente pero que en este caso toma un uso distinto: ``` >>> for i in [0,1,2,3]: ... i+2 ... 2 3 4 5 ``` Como puedes ver, el bloque interno `i+2` se ejecuta en cuatro ocasiones, cambiando el valor de `i` a los valores internos de la lista `[0,1,2,3]`. Cuando la lista termina, la preparación falla porque no quedan elementos y el bucle se rompe. Este último ejemplo es una receta de amplio uso que te aconsejo memorizar. #### Truthy and Falsey En este tipo de sentencias donde se comprueba si una condición es verdadera o falsa, python automáticamente trata de convertir el resultado a Boolean usando la función `bool` que ya conoces del apartado sobre tipos. No es necesario que conviertas a Boolean manualmente ya que estas sentencias disponen de una conversión implícita a Boolean[^truthy]. Por tanto, cualquier resultado que tras pasar por la función `bool` dé como resultado `True` será considerado verdadero y viceversa. A estos valores se les conoce habitualmente como *truthy* y *falsey* porque no son `True` o `False` pero se comportan como si lo fueran. Algunos ejemplos curiosos: ``` >>> bool([]) False >>> bool(None) False >>> bool("") False >>> bool(" ") True >>> bool([None]) True ``` Es relativamente sencillo prever qué valores son *truthy* o *falsey*, normalmente los valores que representan un vacío se consideran `False`. [^truthy]: ### List comprehensions Una de las excepciones sintácticas que sí que podemos explicar en este momento, en el que ya sabes hacer bucles, son las *list comprehensions*. Python dispone de un sistema para crear secuencias y transformarlas muy similar a la notación de construcción de sets de las matemáticas[^set-notation]. Como mejor se entiende es con unos ejemplos, en este caso vamos usar la función `range` para crear una lista de números del `0` (inclusive) al `10` (no inclusive). Usando la ayuda puedes saber más sobre la función `range`. ``` >>> [i**2 for i in range(0, 10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> tuple(i**2 for i in range(0, 10)) (0, 1, 4, 9, 16, 25, 36, 49, 64, 81) >>> { str(i): i**2 for i in range(0, 10)} {'0': 0, '1': 1, '2': 4, '3': 9, '4': 16, '5': 25, '6': 36, '7': 49, '8': 64, '9': 81} >>> [i**2 for i in range(0, 10) if i > 5 ] [36, 49, 64, 81] ``` Como ves, en el caso de los diccionarios es necesario crear las claves también. En este caso las creamos convirtiendo el propio número a string con la función `str`. En los primeros ejemplos, de una secuencia de números hemos creado una secuencia de números al cuadrado. Pero las *list comprehensions* son más poderosas que eso, pudiendo a llegar a complicarse sobremanera añadiendo condiciones, como en el último de los ejemplos, para filtrar algunos casos. Te habrá mosqueado el uso de `tuple` para crear la tupla. Todo tiene una razón, la tupla, al estar formada por paréntesis, python no tiene claro si son paréntesis de precedencia o de creación de tupla, y considera que son los primeros, dando como resultado un generador, un paso intermedio de nuestro proceso que dejamos para el futuro. ``` >>> (i**2 for i in range(0, 10)) at 0x7f779d9b2d58> ``` [^set-notation]: ### Excepciones Las excepciones o *exception* son errores del programa, python lanza excepciones cuando hay problemas. Por ejemplo, cuando intentas acceder a un índice inexistente en una lista. Las excepciones terminan la ejecución del programa a no ser que se gestionen. Se consideran fallos de los que el programa no puede arreglarse a no ser que se le indique cómo. Algunas funciones y librerías lanzan excepciones que nosotros debemos gestionar, por ejemplo: que un archivo no exista, o que no se tenga permisos de edición en el directorio, etc. Es nuestra responsabilidad como programadores tener un plan be o aceptar la excepción deportivamente a sabiendas que nuestro programa terminará indicando un error. Hay ocasiones en las que las excepciones pueden capturarse y otras no, por ejemplo, los fallos de sintaxis no pueden solventarse. Las excepciones se capturan en con un `try-except` que, si programas en otros lenguajes como Java probablemente conozcas como `try-catch`. ```python try: # Bloque donde pueden ocurrir excepciones. except tipo1: # Bloque a ejecutar en caso de que se dé una excepción de tipo1. # Especificar el tipo también es opcional, si no se añade captura todos. except tipoN: # Bloques adicionales (opcionales) a ejecutar en caso de que se dé una # excepción de tipoN, que no haya sido capturada por los bloques # anteriores. finally: # Bloque (opcional) a ejecutar después de lo anterior, haya o no haya # habido excepción. ``` Además de capturarse, las excepciones pueden lanzarse con la sentencia `raise`. ``` python if not input: raise ValueError("Not valid input") ``` Si el ejemplo anterior se diera dentro de una pieza de código que no podemos controlar, podríamos capturar el `ValueError` y evitar que la ejecución de nuestro programa terminara. ``` python try: # Bloque que puede lanzar un ValueError except ValueError: print("Not value in input, using default") input = None ``` Aunque aún no hemos entrado en la programación orientada a objetos, te adelanto que las excepciones se controlan como tal. Hay excepciones que serán hijas de otras, por lo que usando la excepción genérica de la familia seremos capaces de capturarlas, o podremos crear nuevas excepciones como hijas de las que python ya dispone de serie. Por ahora recuerda que debes ordenar los bloques `except` de más concreto a más genérico, porque si lo haces al revés, los primeros bloques te capturarán todas las excepciones y los demás no tendrán ocasión de capturar ninguna, perdiendo así el detalle de los fallos. Cuando aprendas sobre programación orientada a objetos en el apartado correspondiente puedes volver a visitar este punto y leer la documentación de python[^exception] para entender cómo hacerlo. Te adelanto que python tiene una larga lista de excepciones y que está considerado una mala práctica crear nuevas si las excepciones por defecto cubren un caso similar al que se encuentra en nuestro programa. [^exception]: ### Funciones TODO ### Sentencias útiles Python dispone de un conjunto de sentencias que pueden facilitar y flexibilizar mucho el uso de las estructuras que acabamos de visitar. #### Pass `pass` es una sentencia vacía, que no ejecuta nada. Es necesaria debido a las normas de sangría de python. Si construyes un bloque y no quieres rellenarlo por la razón que sea, debes usar `pass` en su interior porque, si no lo haces y simplemente lo dejas vacío, la sintaxis será incorrecta y python lanzará una excepción grave diciéndote que esperaba un bloque con sangría y no se lo diste. ``` >>> if True: ... File "", line 2 ^ IndentationError: expected an indented block >>> if True: ... pass ... ``` Suele utilizarse cuando no quiere tratarse una excepción o cuando se ha hecho un boceto de una función que aún no quiere desarrollarse, para que el intérprete no falle de forma inevitable. > NOTA: Las excepciones de sintaxis son las más graves, implican que el > intérprete no es capaz de entender lo que le pedimos así que la ejecución del > programa no llega a realizarse. La sintaxis se comprueba en una etapa previa > a la ejecución. #### Continue `continue` sirve para terminar el bucle actual y volver a comprobar la condición para decidir si volver a ejecutarlo. En el siguiente ejemplo salta la ejecución para el caso en el que `i` es `2`. ``` >>> for i in [0, 1, 2, 3]: ... if i == 2: ... continue ... i ... 0 1 3 ``` #### Break `break` rompe el bucle actual. A diferencia del `continue`, no se intenta ejecutar la siguiente sentencia, simplemente se rompe el bucle completo. En el siguiente ejemplo se rompe el bucle cuando `i` es `2` y no se recupera. ``` >>> for i in [0, 1, 2, 3]: ... if i == 2: ... break ... i ... 0 1 ``` El `break` se usa mucho para romper bucles que son infinitos por definición. En lugar de añadir una condición compleja a un bucle `while`, por ejemplo, puedes usar un `True` en su condición e introducir un `if` más simple dentro de éste con un `break` que termine el bucle en los casos que te interesen. O puedes capturar una excepción y romper el bucle si ocurre. Además, es muy usado en búsquedas y habilita el uso del `else` en las sentencias `for`. Te propongo como ejercicio que trates de ejecutar y comprender el funcionamiento de esta pieza de código de python avanzado antes de seguir leyendo: ``` python text = "texto de prueba" pos = 0 for i in text: if i == "b": print("Found b in position: " + str(pos)) break pos = pos + 1 else: print("b not found :( ") ``` Si cambias el texto de la variable `text` por uno que no tenga letra `b` verás que el bloque `else` se ejecuta. Esto se debe a que el `for` no termina de forma abrupta, sino que itera por el string completo. Si quieres, puedes memorizar esta estructura para cuando quieras hacer búsquedas. Es elegante y te será útil. #### Return La sentencia `return` sólo tiene sentido dentro de las funciones. Sirve para finalizar la ejecución de una función sustituyendo su llamada por el resultado indicado en el `return`. Esta operación rompe todos los bucles por completo. En el apartado de las funciones profundizaremos en el uso del `return` pero es importante mencionarlo aquí porque su funcionalidad puede sustituir al `break` en muchas ocasiones.