# 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 forma general. 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) 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. Los editores de código, como IDLE, pueden 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. ## 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 para evitar que el programa ejecute ciertas sentencias o repita la ejecución de algunos bloques. 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 no se cumplen todas las condiciones # previas ``` 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 expresiones de python cuyo resultado sea `True` o `False`. Cuando la expresión 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: ``` python >>> 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: ``` python >>> 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]: La documentación oficial de python describe estas conversiones en detalle en la sección *Truth value testing*. ### 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. 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`. ``` python >>> [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) >>> { 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 desde el propio número, así que se comportará de forma similar a una lista, ya que los índices serán numéricos. Eso sí, las claves no estarán ordenadas. 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. ``` python >>> (i**2 for i in range(0, 10)) at 0x7f779d9b2d58> ``` ### 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 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 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("Invalid 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 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. ### Funciones Las funciones sirven, sobre todo, para reutilizar código. Si una pieza de código se utiliza en más de una ocasión en tu programa, es una buena candidata para agruparse en una función y poder reutilizarla sin necesidad de duplicar el código fuente. Aunque sirven para más fines y tienen detalles que aclararemos en un capítulo propio, en este apartado adelantaremos cómo se definen y cómo lanzan y posteriormente las analizaremos en detalle. Las definición de funciones se realiza con una estructura similar a las anteriores, una línea descriptiva terminada en dos puntos (`:`) y después un bloque con mayor sangría para definir el cuerpo. ``` python def nombre_de_funcion (argumentos): """ docstring, un string multilínea que sirve como documentación de la función. Es opcional y no tiene efecto en el funcionamiento de la función. Es lo que se visualiza al ejecutar `help(nombre_de_función)` """ # Cuerpo de la función ``` Para llamar a esta función que acabamos de crear: ``` python nombre_de_función(argumentos) ``` El nombre de la función debe cumplir las mismas normas que los nombres de las referencias de las que ya hemos hablando anteriormente. Y esto debe ser así básicamente porque... ¡el nombre de la función también es una referencia a un valor de tipo función! Pronto ahondaremos más en este tema. De momento recuerda la declaración de las funciones. Dentro de ellas podrás incluir todas las estructuras definidas en este apartado, incluso podrás definir funciones. Aunque en el próximo capítulo tocará hablar de los argumentos de entrada, de momento te adelanto que cuando llamaste a la función `range` anteriormente, le introdujiste dos argumentos. Esos dos argumentos deben declararse como argumentos de entrada. Probablemente de una forma similar a esta: ``` python def range (inicio, fin): contador = inicio salida = [] while contador < fin: salida.append(contador) contador = contador + 1 return salida ``` Lo que va a ocurrir al llamar a la función, por ejemplo, con esta llamada: `range(1, 10)` es que el argumento `inicio` tomará el valor `1` para esta ejecución y el argumento `fin` tomará el valor `10`, como si en el propio cuerpo de la función alguien hubiese hecho: ``` python inicio = 1 fin = 10 ``` El contenido de la función se ejecutará, por tanto, con esas referencias asignadas a un valor. Con lo que sabes ya puedes intentar descifrar el comportamiento de la función `range` que hemos definido, que es similar, pero no igual, a la que define python. Sólo necesitas entender lo que hace la función `list.append` que puedes comprobar en la ayuda haciendo `help( list.append )` y la sentencia `return`, que se explica en el siguiente apartado. Prueba a leer ambas y a crear un archivo de python donde construyes la función y le lanzas unas llamadas. A ver si lo entiendes. ### 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. ``` python >>> 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. > 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`. ``` python >>> 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. ``` python >>> 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. ## Lo que has aprendido Es difícil resumir todo lo que has aprendido en este capítulo porque, la verdad es que es mucha información. Pero no pasa nada porque este capítulo se ha creado más como referencia que como otra cosa. No tengas miedo a volver a leerlo todas las veces que necesites. A todos se nos olvida cómo hay que declarar las funciones si llevamos mucho tiempo sin tocar python, o si era `try-catch` o `try-except`. Este capítulo pretende, por un lado, darte una pincelada de cómo se escribe en python y, por otro, servir como manual de consulta posterior. Pero, por hacer la labor de resumen, has aprendido el orden de ejecución de las órdenes y como alterarlo con bucles y condicionales. Para ello, has tenido que aprender lo que es un *bloque* de código, una pieza fundamental para entender la sintaxis. Tras ver varios ejemplos de bucles y condicionales, te has sumergido en la verdad y la mentira mediante los valores *truthy* y *falsey*, para después avanzar a las *list comprehensions*, cuyo nombre contiene *list* pero valen para cualquier dato complejo. Has cambiado un poco de tema después, saltando a las excepciones, que no has podido ver en detalle por no haber visitado, aún, la programación orientada a objetos. Pero calma, pronto lo haremos. Una pincelada sobre funciones ha sido suficiente para que no les tengas miedo nunca más y que podamos atacar el siguiente capítulo con energía, ya que ahora toca jugar con funciones hasta entenderlas por completo. Las sentencias útiles que hemos recopilado al final permiten juguetear con todas las estructuras que hemos definido en el capítulo de modo que puedas usarlas a tu antojo de forma cómoda. Algunas de ellas como `continue` y `break` no son realmente necesarias, puede programarse evitándolas y, de hecho, en algunos lugares enseñan a no usarlas, como si de una buena práctica se tratara, cambiando las condiciones de los bucles para que hagan esta labor. En este documento se muestran porque, en primer lugar, si lees código escrito por otras personas las encontrarás y tendrás que entender qué hacen y, en segundo, porque son sentencias que simplifican el código haciéndolo más legible o más sencillo por muy impuras que a algunos programadores les puedan parecer.