From e81137474f102d71fe0b25307bcf88810c1b2d43 Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Wed, 4 Mar 2020 13:41:02 +0100 Subject: New arrangement for multilanguage and metadata support --- es/03_estructura.md | 550 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 550 insertions(+) create mode 100644 es/03_estructura.md (limited to 'es/03_estructura.md') diff --git a/es/03_estructura.md b/es/03_estructura.md new file mode 100644 index 0000000..9d8cd5e --- /dev/null +++ b/es/03_estructura.md @@ -0,0 +1,550 @@ +# 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[^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 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 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: + +``` 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]: + + +### 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`. + + +> TODO: +> Mejorar este ejemplo, es demasiado simple + +``` 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) +>>> { 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. + +``` python +>>> (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 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[^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 + +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. + +> 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`. + +``` 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 ser, aún, un experto en programación orientada a +objetos. Pero tranquilo, pronto lo serás. + +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. -- cgit v1.2.3