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 --- src/03_estructura.md | 550 --------------------------------------------------- 1 file changed, 550 deletions(-) delete mode 100644 src/03_estructura.md (limited to 'src/03_estructura.md') diff --git a/src/03_estructura.md b/src/03_estructura.md deleted file mode 100644 index 9d8cd5e..0000000 --- a/src/03_estructura.md +++ /dev/null @@ -1,550 +0,0 @@ -# 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