summaryrefslogtreecommitdiff
path: root/es/06_ejec_mod.md
blob: d28b101bfd34c59d242c1c7f75bace20e5cb5442 (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
# Módulos y ejecución

Hasta ahora, has ejecutado el código en la REPL y de vez en cuando has usado
`F5` en IDLE para ejecutar. Aunque te ha permitido salir del paso, necesitas
saber más en detalle cómo funciona la ejecución para empezar a hacer tus
programas. Además, es absurdo que te pelees contra todo, hay que saber qué
batallas librar, así que necesitarás aprender a importar código realizado por
otras personas para poder centrarte en lo que más te interesa: resolver tu
problema.

Este capítulo trata ambas cosas, que están muy relacionadas, y sirve como
trampolín para el siguiente, la instalación de nuevos paquetes y la gestión de
dependencias, y los posteriores sobre librerías interesantes que te facilitarán
el desarrollo de tus proyectos.

Este capítulo ya te capacita casi al cien por cien para la programación aunque
aún no hemos trabajado su utilidad, pero seguro que alguna idea se te habrá
ocurrido, si no no estarías leyendo este documento.

## Terminología: módulos y paquetes

En python a cualquier fichero de código (extensión `.py`) se le denomina
*módulo* (*module*). A cualquier directorio que contenga módulos de código
python y un fichero llamado `__init__.py`, que puede estar vacío, se le
denomina *paquete* (*package*). El uso del fichero `__init__.py` permite que
python busque módulos dentro del directorio.

Piensa en los paquetes como grupos de módulos que incluso pueden anidarse con
subpaquetes. En la documentación oficial verás en más de una ocasión que se
trata a los paquetes como si fueran módulos, y tiene cierto sentido, porque,
normalmente, existe un módulo principal que permite el acceso a un subpaquete.
Así que, principalmente estás trabajando con un único módulo de un paquete, que
contiene subpaquetes a los que este módulo hace referencia. Aunque pueda sonar
algo enrevesado, no te preocupes por ahora: los módulos son ficheros únicos y
los paquetes conjuntos de ellos.

## Ejecución

Ya conoces un par de maneras de ejecutar tus módulos de python. Usar la REPL,
introduciéndole el código que quieres ejecutar, llamar a la función «ejecutar
módulo» de IDLE con la tecla `F5` e incluso llamar a la shell de sistema con un
comando similar a este:

``` bash
python mi_archivo.py
```

Normalmente, los ficheros de python se ejecutan de este último modo en
producción, mientras que los dos anteriores son más usados a la hora de
desarrollar. Realmente son métodos similares, en todos ellos el intérprete
accede al fichero o contenido que recibe y ejecuta las líneas una a una.

Existe también una opción adicional muy usada que sirve para ejecutar módulos
que el sistema sea capaz de encontrar por sí mismo, en lugar de indicarle la
ruta, se le puede indicar simplemente el nombre del módulo usando la opción
`-m`:

``` bash
python -m nombre_de_modulo
```

## Importación y *namespaces*

Anteriormente hemos pasado sobre la sintaxis de la importación de forma muy
superficial pero tampoco es mucho más compleja a lo que ha aparecido. La
sentencia `import` permite importar diferentes módulos a nuestro programa como
en el siguiente ejemplo:

``` python
>>> import datetime
>>> datetime
<module 'datetime' from '/usr/lib/python3.6/datetime.py'>
```

Han pasado, sin embargo, muchas cosas interesantes en el ejemplo. En primer
lugar, python ha buscado y encontrado el módulo `datetime` en el sistema y, en
segundo lugar, ha creado un objeto módulo llamado `datetime` que atesora todas
las definiciones globales del módulo `datetime`.

Empezando por el final, python usa lo que se conoce como *namespace* de forma
muy extendida. Los *namespaces*, de nombre (*name*) y espacio (*space*), son
una herramienta para separar contextos ampliamente usada. Los objetos, en
realidad, son una caso de *namespace* ya que cuando se llama a un método se le
dice cuál es el contexto de la llamada, es decir: al método de qué objeto se
llama.

Para los módulos el proceso es el mismo. La sentencia `import` trae un módulo
al programa pero lo esconde tras su *namespace*, de este modo, para acceder a
algo definido en el recién importado módulo es necesario indicarle el nombre de
éste de la siguiente manera:

``` python
>>> import datetime
>>> datetime.date.today()
datetime.date(2019, 12, 3)
```

En el ejemplo se accede a la clase `date` dentro del módulo `datetime`, y se
lanza su función `today`, que indica el día de hoy. Como puedes apreciar, el
operador `.` se utiliza del mismo modo que en las clases y objetos, y en
realidad es difícil saber cuándo se está accediendo a una clase y cuándo a un
módulo, aunque tampoco es necesario saberlo.

Para no tener que escribir el nombre del módulo completo, existe otra versión
de la sentencia `import` que tiene un comportamiento muy similar:

``` python
>>> import datetime as dt
>>> dt.date.today()
datetime.date(2019, 12, 3)
```

En este ejemplo, se ha cambiado el nombre del módulo a uno más corto decidido
por el programador, `dt`. El funcionamiento es el mismo, simplemente se ha
cambiado el nombre para simplificar. Este cambio de nombre también es útil
cuando se va a importar un módulo cuyo nombre es igual que alguna otra
definición. Cambiando el nombre se evitan colisiones.

Existen versiones además, que permiten importar únicamente las funciones y
clases seleccionadas, pero que las añaden al *namespace* actual, para evitar
tener que usar el prefijo.

``` python
>>> from datetime import date
>>> date.today()
datetime.date(2019, 12, 3)
```

En este último ejemplo, se trae la clase `date` al contexto actual. También
existe la posibilidad de importar más de una definición del módulo, usando la
coma para separarlas, o todo lo que el módulo exponga mediante el símbolo `*`.
Es peligroso, sin embargo, traer definiciones al namespace actual de forma
descuidada, sobre todo con la última opción, porque es posible que se repitan
nombres por accidente y se pisen definiciones. Los namespaces se inventan con
el fin de separar las definiciones y evitar colisiones de este tipo.

### Búsqueda

Una vez descrito cómo se interactúa con los módulos importados, es necesario
describir dónde se buscan estos módulos.

Los módulos se buscan en los siguientes lugares:

1. El directorio del fichero ejecutado o el directorio de trabajo de la REPL
2. Los directorios indicados en el entorno
3. La configuración por defecto (depende de la instalación)

Esto significa que si guardas un archivo de python en IDLE y guardas otro más
en el mismo directorio con el nombre `modulo.py` podrás importarlo usando
`import modulo` en el primero, ya que comparten directorio. Lo mismo ocurre con
los paquetes, crear un directorio con nombre `paquete` y añadirle un fichero
vacío llamado `init.py` te permitirá hacer `import paquete`. Si añadieras más
módulos dentro del paquete, podrías importar cada uno de ellos mediante
`paquete.modulo`.

>  Los nombres de los ficheros deben coincidir con el el nombre del módulo
> más la extensión `.py`. En el caso de los directorios, saltar a un
> subdirectorio implica acceder a un paquete, por lo que se añadirá un punto
> (`.`).

El primer punto sirve para facilitar que organices tu proyecto de python en
varios módulos, separando así la funcionalidad en diferentes archivos.

Los últimos dos puntos son los que permiten a python encontrar su librería
estándar y los módulos de sistema. El tercero depende de la instalación y del
formato de ésta: si python está instalado como portable no será igual que si se
instala en el sistema del modo habitual. El segundo punto también puede variar
de un sistema a otro, pero en resumen se trata de varias variables de entorno
de sistema que le indican a python dónde buscar (normalmente toman el nombre
`PYTHONPATH`, pero no es siempre así). El segundo punto puede alterarse de modo
que en función de lo que se le indique, se puede pedir a python que busque los
módulos en un lugar u otro.

Estos lugares de búsqueda se pueden mostrar de la siguiente manera:

``` python
>>> import sys
>>> print(sys.path)
[ '',
  '/usr/lib/python36.zip',
  '/usr/lib/python3.6',
  '/usr/lib/python3.6/lib-dynload',
  '/usr/local/lib/python3.6/dist-packages',
  '/usr/lib/python3/dist-packages']
```

En función del sistema en el que te encuentres y la configuración que tengas,
python mostrará diferente resultado.

Rescatando un ejemplo previo:

``` python
>>> import datetime
>>> datetime
<module 'datetime' from '/usr/lib/python3.6/datetime.py'>
```

Ahora entiendes por qué es capaz de encontrar `datetime` en
`/usr/lib/python3.6`, carpeta listada en `sys.path`, bajo el nombre
`datetime.py`.

## Ejecución e importación

A la hora de importar un módulo, python procesa el contenido de éste ya que
necesita definir las funciones, clases, valores, etc. a exportar. Para poder
hacerlo, python necesita ejecutar el módulo.

Python define una forma de separar la funcionalidad del código de sus
definiciones con el fin de poder crear código cuyas definiciones sean
reutilizables mediante la importación en otro módulo, sin que tenga ninguna
funcionalidad cuando esto ocurra, pero habilitando que tenga funcionalidades
cuando sea llamado directamente.

Un ejemplo de uso de esto puede ser un módulo de acceso a ficheros, por
ejemplo, que visualice el contenido del fichero cuando se llame de forma
directa pero que cuando se importe únicamente aporte las funciones de lectura y
escritura sin leer y mostrar ningún fichero.

Para que los módulos puedan tener esta doble vida, python define la variable
`__name__` que representa en qué nivel del *scope* se está ejecutando el módulo
actual. La variable `__name__` toma el valor del nombre del módulo cuando éste
está siendo importado y el valor `__main__` cuando ha sido llamado de forma
directa o está siendo ejecutado en la REPL. `__main__` es el *scope* global de
los programas, por lo que cuando algo se declara en él, implica que es el
programa principal.

Para poder diferenciar cuándo se ha ejecutado un módulo de forma directa y
cuando se ha importado se utiliza lo que se conoce como `__main__` *guard*:

``` python
if __name__ == "__main__":
    # Este bloque sólo se ejecuta cuando el módulo es el principal
```

Aunque igual es un poco incómodo de entender de primeras, encontrarás esta
estructura en casi cualquier módulo de código python. Se utiliza
constantemente, incluso para los casos en los que no se pretende que el código
pueda importarse. Es una buena práctica incluir el *guard* para separar la
ejecución de las definiciones, de este modo, quien quiera saber cuál es la
funcionalidad del módulo tendrá mucho más fácil la búsqueda.

Puedes leer más sobre este tema en la documentación de python[^main-guard].

[^main-guard]: <https://docs.python.org/3/library/__main__.html>


Siguiendo este concepto, también existe un estándar de nomenclatura de
ficheros. El nombre `__main__.py` hace referencia al fichero que contiene el
código principal del programa y será el fichero que python buscará ejecutar
siempre que se le pida ejecutar un paquete o un directorio sin especificar qué
módulo debe lanzar. Por ejemplo, ejecutar `python .`[^dot] en la shell de
sistema es equivalente a ejecutar `python __main__.py`.

[^dot]: `.` significa directorio actual en cualquiera de los sistemas
  operativos comunes.


## Lo que has aprendido

En este capítulo corto has aprendido lo necesario sobre importación de módulos
y ejecución de código. Conocer en detalle el patrón de búsqueda de módulos de
python es primordial para evitar problemas en el futuro y organizar los
proyectos de forma elegante.

Además, el `__main__` *guard* era una de las últimas convenciones de uso común
que quedaban por explicar y una vez vista ya eres capaz de leer proyectos de
código fuente que te encuentres por ahí sin demasiados problemas.

Los próximos capítulos, basándose en lo aprendido en éste, te mostrarán cómo
instalar nuevas dependencias y cómo preparar tu propio código para que pueda
ser instalado de forma limpia y elegante.