Creación de módulos propios
Introducción
Después de haber aprendido a utilizar módulos existentes, el siguiente paso natural es crear nuestros propios módulos. Esta capacidad es fundamental para cualquier programador Python, ya que nos permite organizar nuestro código en componentes reutilizables y mantener nuestros proyectos ordenados. En este artículo, aprenderemos cómo crear, estructurar y utilizar nuestros propios módulos, lo que nos permitirá desarrollar proyectos más organizados y profesionales.
¿Por qué crear módulos propios?
Crear nuestros propios módulos ofrece numerosas ventajas:
- Reutilización de código: Evitamos duplicar código entre diferentes programas
- Organización: Mantenemos funcionalidades relacionadas agrupadas lógicamente
- Mantenimiento: Es más fácil actualizar y corregir errores en un solo lugar
- Legibilidad: Nuestros programas principales quedan más limpios y concisos
- Colaboración: Facilitamos el trabajo en equipo al modularizar las partes del proyecto
Creación de un módulo básico
Crear un módulo en Python es extremadamente sencillo: basta con escribir el código en un archivo con extensión .py
. Veamos un ejemplo:
- Creamos un archivo llamado
operaciones.py
con el siguiente contenido:
# operaciones.py
# Módulo con funciones matemáticas básicas
def suma(a, b):
"""Suma dos números y devuelve el resultado"""
return a + b
def resta(a, b):
"""Resta b de a y devuelve el resultado"""
return a - b
def multiplicacion(a, b):
"""Multiplica dos números y devuelve el resultado"""
return a * b
def division(a, b):
"""Divide a entre b y devuelve el resultado"""
if b == 0:
raise ValueError("No se puede dividir entre cero")
return a / b
# Variable del módulo
PI = 3.14159265359
- Ahora podemos importar y usar este módulo en otro archivo Python:
# programa.py
import operaciones
# Uso de las funciones del módulo
resultado1 = operaciones.suma(5, 3)
resultado2 = operaciones.multiplicacion(4, 2)
print(f"Suma: {resultado1}") # Imprime: Suma: 8
print(f"Multiplicación: {resultado2}") # Imprime: Multiplicación: 8
print(f"Valor de PI: {operaciones.PI}") # Imprime: Valor de PI: 3.14159265359
Estructuración del módulo
Un módulo bien estructurado debe:
- Comenzar con un docstring que describa el propósito del módulo
- Incluir las importaciones que el módulo necesita
- Contener funciones y clases relacionadas con la temática del módulo
- Definir variables globales si son necesarias
- Incorporar código de prueba opcional (lo veremos más adelante)
Ejemplo mejorado:
"""
Módulo de operaciones matemáticas
Este módulo proporciona funciones básicas para realizar
operaciones aritméticas entre números.
"""
# Importaciones
import math
# Constantes
PI = 3.14159265359
E = 2.71828
# Funciones
def suma(a, b):
"""Suma dos números y devuelve el resultado"""
return a + b
def resta(a, b):
"""Resta b de a y devuelve el resultado"""
return a - b
# [...más funciones...]
Docstrings en módulos y funciones
Es una buena práctica incluir documentación tanto para el módulo como para cada función. Los docstrings son cadenas de texto que describen el propósito y uso de módulos, clases y funciones:
"""
Este es un docstring de módulo que explica su propósito general.
"""
def calcular_area_circulo(radio):
"""
Calcula el área de un círculo.
Args:
radio (float): El radio del círculo
Returns:
float: El área del círculo
Raises:
ValueError: Si el radio es negativo
"""
if radio < 0:
raise ValueError("El radio no puede ser negativo")
return PI * radio ** 2
Estos docstrings pueden ser consultados mediante la función help()
:
import mi_modulo
help(mi_modulo) # Muestra el docstring del módulo
help(mi_modulo.calcular_area_circulo) # Muestra el docstring de la función
Ejecución condicional con __name__
Una característica muy útil de Python es la variable especial __name__
, que permite que un archivo funcione tanto como módulo importable como programa independiente:
# geometria.py
def calcular_area_rectangulo(base, altura):
return base * altura
def calcular_perimetro_rectangulo(base, altura):
return 2 * (base + altura)
# Código que se ejecuta solo cuando el archivo se ejecuta directamente
if __name__ == "__main__":
print("Pruebas del módulo geometría")
print(f"Área de un rectángulo 5x3: {calcular_area_rectangulo(5, 3)}")
print(f"Perímetro de un rectángulo 5x3: {calcular_perimetro_rectangulo(5, 3)}")
Cuando importamos este módulo, el código bajo if __name__ == "__main__":
no se ejecuta:
import geometria
# Aquí no se mostrarán los mensajes de prueba
Pero cuando ejecutamos el módulo directamente (python geometria.py
), la condición es verdadera y se ejecuta el código de prueba.
Importaciones relativas vs absolutas
Python ofrece dos formas de importar módulos propios:
Importaciones absolutas
Usan la ruta completa del módulo:
from paquete.subpaquete import modulo
Importaciones relativas
Usan puntos para indicar la posición relativa:
from . import modulo_hermano # Módulo en el mismo directorio
from .. import modulo_padre # Módulo en el directorio padre
from ..hermano import modulo # Módulo en directorio hermano
Las importaciones relativas solo funcionan dentro de paquetes (no en scripts independientes).
Controlar qué se importa con __all__
Podemos controlar qué elementos se importan cuando alguien usa from modulo import *
mediante la variable __all__
:
# mi_modulo.py
__all__ = ['funcion_publica', 'CONSTANTE_PUBLICA']
# Esta función será importada con from mi_modulo import *
def funcion_publica():
pass
# Esta función NO será importada con from mi_modulo import *
def _funcion_interna():
pass
# Esta constante será importada con from mi_modulo import *
CONSTANTE_PUBLICA = 100
Organización de módulos en paquetes
Para proyectos más grandes, podemos organizar nuestros módulos en paquetes:
mi_proyecto/
├── __init__.py
├── modulo1.py
├── modulo2.py
└── subpaquete/
├── __init__.py
└── modulo3.py
El archivo __init__.py
(que puede estar vacío) le indica a Python que la carpeta debe tratarse como un paquete. También podemos usarlo para inicializar el paquete:
# __init__.py del paquete principal
from .modulo1 import funcion_importante
from .modulo2 import otra_funcion
# Esto permite usar: from mi_proyecto import funcion_importante
Ejemplo completo de un paquete
Veamos un ejemplo de una estructura de calculadora:
calculadora/
├── __init__.py
├── operaciones_basicas.py
└── operaciones_avanzadas.py
Contenido de los archivos:
# calculadora/__init__.py
"""
Paquete calculadora - Proporciona funciones matemáticas básicas y avanzadas
"""
from .operaciones_basicas import suma, resta, multiplicacion, division
# calculadora/operaciones_basicas.py
"""
Módulo con operaciones matemáticas básicas
"""
def suma(a, b):
return a + b
def resta(a, b):
return a - b
def multiplicacion(a, b):
return a * b
def division(a, b):
if b == 0:
raise ValueError("No se puede dividir entre cero")
return a / b
# calculadora/operaciones_avanzadas.py
"""
Módulo con operaciones matemáticas avanzadas
"""
import math
def potencia(base, exponente):
return base ** exponente
def raiz_cuadrada(numero):
if numero < 0:
raise ValueError("No se puede calcular la raíz de un número negativo")
return math.sqrt(numero)
Uso del paquete:
# Importación de funciones individuales
from calculadora import suma, resta
resultado = suma(10, 5)
# Importación de módulo específico
import calculadora.operaciones_avanzadas as avanzadas
resultado = avanzadas.potencia(2, 8)
Buenas prácticas para crear módulos
- Mantén los módulos enfocados: Cada módulo debe tener una responsabilidad clara
- Usa nombres descriptivos: Los nombres deben indicar la función del módulo
- Incluye docstrings: Documenta tanto el módulo como sus funciones
- Mantén la coherencia: Usa un estilo de programación coherente
- Prueba tus módulos: Incluye código de prueba usando
if __name__ == "__main__"
- Evita efectos secundarios: Los módulos no deberían ejecutar código importante al importarse
- Sigue la guía de estilo PEP 8: Usa convenciones estándar de Python
Resumen
Crear nuestros propios módulos es una habilidad esencial que nos permite organizar, reutilizar y compartir nuestro código de manera efectiva. Al dominar la creación de módulos, estaremos dando un gran paso hacia el desarrollo de aplicaciones Python más profesionales y mantenibles. En el próximo artículo, profundizaremos en cómo organizar nuestros módulos en paquetes para proyectos más complejos.