summaryrefslogtreecommitdiff
path: root/es/03_estructura.md
blob: 9c24fb171b0fe7626acfd4ad9baa6f088774b218 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# 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 cuáles 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.
En 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>
```

### 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 se puede recuperar 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 en las que
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.

La 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.

>  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.