From 44bc3919b6c995e7b08826a0eca8aef5a22e43e7 Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Thu, 21 Nov 2019 13:54:50 +0100 Subject: WIP: language structure --- src/03_estructura.md | 371 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 src/03_estructura.md (limited to 'src') diff --git a/src/03_estructura.md b/src/03_estructura.md new file mode 100644 index 0000000..67fe260 --- /dev/null +++ b/src/03_estructura.md @@ -0,0 +1,371 @@ +# 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, + +``` +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. + + +#### NOTA: 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`. + +Como ves 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 + +TODO + +### 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 sirve para saltar 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. -- cgit v1.2.3