Módulos¶

Imagen generada con Inteligencia Artificial
Escribir pequeños trozos de código puede resultar interesante para realizar determinadas pruebas o funcionalidades sencillas. Pero a la larga, nuestros programas tenderán a crecer y será necesario agrupar el código en piezas/artefactos más manejables.
Módulos¶
Un módulo en Python es simplemente un fichero de texto que contiene código fuente. Permite evitar la repetición y favorece la reutilización.
Los módulos pueden agruparse en carpetas denominadas paquetes mientras que estas carpetas, a su vez, pueden dar lugar a librerías.
Un ejemplo de todo ello lo encontramos en la librería estándar. Se trata de una librería que ya viene incorporada en Python y que, a su vez, dispone de una serie de paquetes que incluyen distintos módulos.
Un caso concreto dentro de la stdlib (libería estándar) podría ser el del paquete urllib —para operaciones con URLs— que dispone de 5 módulos:
Importar un módulo completo¶
Para hacer uso del código de otros módulos usaremos la sentencia import. Permite importar el código y las variables de dicho módulo para tenerlas disponibles en nuestro programa.
La forma más sencilla de importar un módulo es import module donde module es el nombre de otro fichero Python, sin la extensión .py.
Partiremos del siguiente ejemplo donse se ha implementado un módulo para cálculos estadísticos:
def mean(*values: int | float) -> float:
"""Calculate mean of values"""
return sum(values) / len(values)
def std(*values: int | float) -> float:
"""Calculate standard deviation of values"""
m = mean(*values)
p = sum((v - m) ** 2 for v in values)
return (p / (len(values) - 1)) ** (1 / 2)
Desde otro fichero (en principio en la misma carpeta) podríamos importar el contenido de stats.py para hacer uso de sus funcionalidades:
- Es necesario anteponer a la función
mean()el espacio de nombres que define el módulostats. - Es necesario anteponer a la función
std()el espacio de nombres que define el módulostats.
Librería estándar
En el caso de utilizar un módulo de la librería estándar, basta con saber su nombre e importarlo directamente:
Ruta de búsqueda de módulos¶
Cuando importamos un módulo en Python el intérprete trata de encontrarlo (por orden) en las rutas definidas dentro de la variable sys.path.
Veamos el contenido de la variable sys.path para el caso concreto de mi entorno de desarrollo:
- L4 Comandos ejecutables del entorno virtual.
- L5 Contiene módulos de la librería estándar en formato comprimido.
- L6 Carpeta con módulos adicionales que no pueden estar comprimidos (por ejemplo, archivos compilados en C).
- L7 Contiene las extensiones compartidas (
.so,.pyd) que no pueden ejecutarse desde un ZIP. - L8 Representa el directorio (carpeta) actual.
- L9 Paquetes instalados en el entorno virtual.
Podemos modificar la ruta de búsqueda de paquetes Python. Para ello existen dos opciones:
Modificando directamente la variable de entorno PYTHONPATH:
Comprobamos que se ha modificado en sys.path:
>>> import sys
>>> sys.path
['/Users/sdelquin/code/personal/aprendepython-mkdocs/.venv/bin',
'/tmp',
'/Users/sdelquin/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python313.zip',
'/Users/sdelquin/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python3.13',
'/Users/sdelquin/.local/share/uv/python/cpython-3.13.2-macos-aarch64-none/lib/python3.13/lib-dynload',
'',
'/Users/sdelquin/code/personal/aprendepython-mkdocs/.venv/lib/python3.13/site-packages']
Orden de rutas
El hecho de poner nuestra ruta al principio o al final de sys.path influye en la búsqueda, ya que si existen dos (o más módulos) que se llaman igual en nuestra ruta de búsqueda, Python usará el primero que encuentre.
Importar partes de un módulo¶
Es posible que no necesitemos todo aquello que está definido en stats.py. Por ejemplo sólo vamos a calcular medias. En este caso la importación se hace de manera diferente:
- La función se utiliza sin ningún prefijo.
Este esquema tiene el inconveniente de la posible colisión de nombres, en aquellos casos en los que tuviéramos algún objeto con el mismo nombre que el objeto que estamos importando. Para estas (u otras) situaciones Python permite usar alias a través de la sentencia as:
Para importar varios objetos (funciones en este caso) desde un mismo módulo, podemos especificarlos separados por comas en la misma línea:
Es posible hacer from stats import * pero estaríamos importando todos los componentes del módulo, cuando a lo mejor no es lo que necesitamos. A continuación una imagen que define bien este escenario:
Paquetes¶
Un paquete es simplemente una carpeta que contiene ficheros .py. Además permite tener una jerarquía con más de un nivel de subcarpetas anidadas.
Pongamos un ejemplo creando un paquete extramath que contendrá dos módulos:
-
stats.pypara cálculos estadísticos. -
ofrac.pypara operaciones auxiliares con fracciones.
El módulo stats.py ya se definió previamente aquí y el código del módulo ofrac.py se presenta a continuación:
def gcd(a: int, b: int) -> int:
"""Greatest common divisor through Euclides Algorithm"""
while b > 0:
a, b = b, a % b
return a
def lcm(a: int, b: int) -> int:
"""Least common multiple through Euclides Algorithm"""
return a * b // gcd(a, b)
Si nuestro código principal va a estar en main.py (a primer nivel), la estructura de ficheros nos quedaría tal que así:
- Punto de entrada de nuestro programa a partir del fichero
main.py - Carpeta que define el paquete
extramath. - Módulo para operaciones auxiliares de fracciones.
- Módulo para cálculos estadísticos.
Si lo ponemos todo junto, nos quedaría un esquema como el siguiente:
flowchart LR
subgraph Package
E@{ shape: docs, label: "extramath"}
end
subgraph Module
O@{ shape: delay, label: "ofrac"}
S@{ shape: delay, label: "stats"}
end
subgraph Function
gcd["<tt>gcd()</tt>"]
lcm["<tt>lcm()</tt>"]
mean["<tt>mean()</tt>"]
std["<tt>std()</tt>"]
end
E --> O
E --> S
O --> gcd
O --> lcm
S --> mean
S --> std
Importar desde un paquete¶
Si ya estamos en el fichero main.py (o a ese nivel) podremos hacer uso de nuestro paquete de la siguiente forma:
>>> from extramath import frac, stats#(1)!
>>> frac.gcd(21, 35)#(2)!
7
>>> stats.mean(6, 3, 9, 5)#(3)!
5.75
- Importar los módulos
fracystatsdel paqueteextramath - Uso de la función
gcdque está definida en el módulofrac - Uso de la función
meanque está definida en el módulostats
Programa principal¶
Cuando decidimos desarrollar un artefacto de software en Python, normalmente usamos distintos ficheros para ello. Algunos de esos ficheros se convertirán en módulos, otros se englobarán en paquetes y existirá uno en concreto que será nuestro punto de entrada, también llamado programa principal.
La anatomía de un programa principal (habitualmente llamado main.py) es la siguiente:
# imports de la librería estándar
# imports de librerías de terceros
# imports de módulos propios
# CÓDIGO PROPIO
# ...
if __name__ == '__main__':
# punto de entrada real
Si queremos ejecutar este fichero main.py desde línea de comandos, tendríamos que hacer:
Punto de entrada¶
Tratemos de explicar cuál es el cometido de la sentencia: if __name__ == '__main__'
Esta condición permite, en el programa principal, diferenciar qué codigo se lanzará cuando el fichero se ejecuta directamente o cuando el fichero se importa desde otro lugar.
La variable __name__ puede tomar dos posibles valores:
- El nombre del módulo (o paquete) al importar el fichero.
- El valor
'__main__'al ejecutar el fichero.
Veamos el siguiente ejemplo con el programa hello.py y analicemos su comportamiento según el escenario escogido:
| hello.py | |
|---|---|
-
import hello
Se ejecutan las siguientes líneas (desde arriba hacia abajo):
L1 se importa el módulo
blabla.
L4 se define la funciónmyfuncy estará disponible para usarse.
L9 esta condición no se cumple, ya que estamos importando y la variable especial__name__no toma ese valor. Con lo cual finaliza la ejecución.No hay salida por pantalla.
-
python main.py
Se ejecutan las siguientes líneas (desde arriba hacia abajo):
L1 se importa el módulo
blabla.
L4 se define la funciónmyfuncy estará disponible para usarse.
L9 esta condición sí se cumple, ya que estamos ejecutando directamente el fichero (como programa principal) y la variable especial__name__toma el valor'__main__'.
L10 Entry point
L11 llamada a la funciónmyfunc().
L5 Inside myfunc
L6 llamada a la funciónhi()del móduloblabla.