Ir al contenido principal

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:

  1. 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
  1. 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:

  1. Comenzar con un docstring que describa el propósito del módulo
  2. Incluir las importaciones que el módulo necesita
  3. Contener funciones y clases relacionadas con la temática del módulo
  4. Definir variables globales si son necesarias
  5. 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

  1. Mantén los módulos enfocados: Cada módulo debe tener una responsabilidad clara
  2. Usa nombres descriptivos: Los nombres deben indicar la función del módulo
  3. Incluye docstrings: Documenta tanto el módulo como sus funciones
  4. Mantén la coherencia: Usa un estilo de programación coherente
  5. Prueba tus módulos: Incluye código de prueba usando if __name__ == "__main__"
  6. Evita efectos secundarios: Los módulos no deberían ejecutar código importante al importarse
  7. 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.