summaryrefslogtreecommitdiff
path: root/src/03_estructura.md
blob: d9a046dd3c8b41ed81f7df906575be992e8ba6d8 (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
# 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]: <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 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,

``` 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:

```
>>> 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:

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

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

```
>>> (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 en 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("Not valid 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

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

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