summaryrefslogtreecommitdiff
path: root/es/03_estructura.md
diff options
context:
space:
mode:
authorEkaitz Zarraga <ekaitz@elenq.tech>2020-03-04 13:41:02 +0100
committerEkaitz Zarraga <ekaitz@elenq.tech>2020-03-04 13:41:02 +0100
commite81137474f102d71fe0b25307bcf88810c1b2d43 (patch)
tree840a184cf0b9c08a84c25caf390e4b7696589c9a /es/03_estructura.md
parent403a94590f02479f7be41bbb62aae972e0ab596a (diff)
New arrangement for multilanguage and metadata support
Diffstat (limited to 'es/03_estructura.md')
-rw-r--r--es/03_estructura.md550
1 files changed, 550 insertions, 0 deletions
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]: <https://en.wikipedia.org/wiki/Indentation_(typesetting)>
+
+## 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]: <https://docs.python.org/3/library/stdtypes.html#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[^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))
+<generator object <genexpr> at 0x7f779d9b2d58>
+```
+
+[^set-notation]: <https://en.wikipedia.org/wiki/Set-builder_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]: <https://docs.python.org/3/library/exceptions.html>
+
+### 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 "<stdin>", 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.