Link Search Menu Expand Document

Módulos en Python

Introducción

Un módulo o module en Python es un fichero .py que alberga un conjunto de funciones, variables o clases y que puede ser usado por otros módulos. Nos permiten reutilizar código y organizarlo mejor en namespaces. Por ejemplo, podemos definir un módulo mimodulo.py con dos funciones suma() y resta().

# mimodulo.py
def suma(a, b):
    return a + b

def resta(a, b):
    return a - b

Una vez definido, dicho módulo puede ser usado o importado en otro fichero, como mostramos a continuación. Usando import podemos importar todo el contenido.

# otromodulo.py
import mimodulo

print(mimodulo.suma(4, 3))   # 7
print(mimodulo.resta(10, 9)) # 1

También podemos importar únicamente los componentes que nos interesen como mostramos a continuación.

from mimodulo import suma, resta

print(suma(4, 3))   # 7
print(resta(10, 9)) # 1

Por último, podemos importar todo el módulo haciendo uso de *, sin necesidad de usar mimodulo.*.

from mimodulo import *

print(suma(4, 3))   # 7
print(resta(10, 9)) # 1

Rutas y Uso de sys.path

Normalmente los módulos que importamos están en la misma carpeta, pero es posible acceder también a módulos ubicados en una subcarpeta. Imaginemos la siguiente estructura:

.
├── ejemplo.py
├── carpeta
│   └── modulo.py

Donde modulo.py contiene lo siguiente:

# modulo.py
def hola():
	print("Hola")

Desde nuestro ejemplo.py, podemos importar el módulo modulo.py de la siguiente manera:

from carpeta.modulo import *
print(hola())
# Hola

Es importante notar que Python busca los módulos en las rutas indicadas por el sys.path. Es decir, cuando se importa un módulo, lo intenta buscar en dichas carpetas. Puedes ver tu sys.path de la siguiente manera:

import sys
print(sys.path)

Como es obvio, verás que la carpeta de tu proyecta está incluida, pero ¿y si queremos importar un módulo en una ubicación distinta? Pues bien, podemos añadir al sys.path la ruta en la que queremos que Python busque.

import sys
sys.path.append(r'/ruta/de/tu/modulo')

Una vez realizado esto, los módulos contenidos en dicha carpeta podrán ser importados sin problema como hemos visto anteriormente.

Cambiando los Nombres con as

Por otro lado, es posible cambiar el nombre del módulo usando as. Imaginemos que tenemos un módulo moduloconnombrelargo.py.

# moduloconnombrelargo.py
hola = "hola"

En vez de usar el siguiente import, tal vez queramos asignar un nombre más corto al módulo.

import moduloconnombrelargo
print(moduloconnombrelargo.hola)

Podemos hacerlo de la siguiente manera con as:

import moduloconnombrelargo as m
print(m.hola)

## Listando dir

La función dir() nos permite ver los nombres (variables, funciones, clases, etc) existentes en nuestro namespace. Si probamos en un módulo vacío, podemos ver como tenemos varios nombres rodeados de __. Se trata de nombres que Python crea por debajo.

print(dir())
# ['__annotations__', '__builtins__', '__cached__', '__doc__',
# '__file__', '__loader__', '__name__', '__package__', '__spec__']

Por ejemplo, __file__ es creado automáticamente y alberga el nombre del fichero .py.

print(__file__)
#/tu/ruta/tufichero.py

Imaginemos ahora que tenemos alguna variable y función definida en nuestro script. Como era de esperar, dir() ahora nos muestra también los nuevos nombres que hemos creado, y que por supuesto pueden ser usados.

mi_variable = "Python"
def mi_funcion():
    pass

print(dir())

#['__annotations__', '__builtins__', '__cached__', '__doc__',
# '__file__', '__loader__', '__name__', '__package__', '__spec__',
# 'mi_funcion', 'mi_variable']

Por último, vamos a importar el contenido de un módulo externo. Podemos ver que en el namespace tenemos también los nombres resta y suma, que han sido tomados de mimodulo.

from mimodulo import *
print(dir())

# ['__annotations__', '__builtins__', '__cached__',
# '__doc__', '__file__', '__loader__', '__name__',
# '__package__', '__spec__', 'resta', 'suma']

El uso de dir() también acepta parámetros de entrada, por lo que podemos por ejemplo pasar nuestro modulo y nos dará más información sobre lo que contiene.

import mimodulo

print(dir(mimodulo))
# ['__builtins__', '__cached__', '__doc__',
# '__file__','__loader__', '__name__',
# '__package__', '__spec__', 'resta', 'suma']

print(mimodulo.__name__)
# mimodulo

print(mimodulo.__file__)
# /tu/ruta/mimodulo.py

Excepción ImportError

Importar un módulo puede lanzar una excepción, cuando se intenta importar un módulo que no ha sido encontrado. Se trata de ModuleNotFoundError.

import moduloquenoexiste
# ModuleNotFoundError: No module named 'moduloquenoexiste'

Dicha excepción puede ser capturada para evitar la interrupción del programa.

try:
    import moduloquenoexiste
except ModuleNotFoundError as e:
    print("Hubo un error:", e)

Módulos y Función Main

Un problema muy recurrente es cuando creamos un módulo con una función como en el siguiente ejemplo, y añadimos algunas sentencias a ejecutar.

# modulo.py

def suma(a, b):
    return a + b

c = suma(1, 2)
print("La suma es:", c)

Si en otro módulo importamos nuestro modulo.py, tal como está nuestro código el contenido se ejecutará, y esto puede no ser lo que queramos.

# otromodulo.py
import modulo

# Salida: La suma es: 3

Dependiendo de la situación, puede ser importante especificar que únicamente queremos que se ejecute el código si el módulo es el __main__. Con la siguiente modificación, si hacemos import modulo desde otro módulo, este fragmento ya no se ejecutará al ser el módulo main otro.

# modulo.py
def suma(a, b):
    return a + b

if (__name__ == '__main__'):
    c = suma(1, 2)
    print("La suma es:", c)

Recargando Módulos

Es importante notar que los módulos solamente son cargados una vez. Es decir, no importa el número de veces que llamemos a import mimodulo, que sólo se importará una vez.

Imaginemos que tenemos el siguiente módulo que imprime el siguiente contenido cuando es importado.

# mimodulo.py

print("Importando mimodulo")

def suma(a, b):
    return a + b

def resta(a, b):
    return a - b

A pesar de que llamamos tres veces al import, sólo vemos una única vez el contenido del print.

import mimodulo
import mimodulo
import mimodulo
# Importando mimodulo

Si queremos que el módulo sea recargado, tenemos que ser explícitos, haciendo uso de reload.

import mimodulo
import importlib
importlib.reload(mimodulo)
importlib.reload(mimodulo)