HDP115

Comentarios y Documentación

Aprende a documentar tu código Python correctamente utilizando comentarios, docstrings y buenas prácticas para crear programas mantenibles y comprensibles.

CE

Cristian Escalante

Última actualización: 19 de mayo de 2025

python
programación
documentación

Comentarios y Documentación en Python

La documentación adecuada es una parte esencial del desarrollo de software. Un código bien documentado es más fácil de entender, mantener y depurar, tanto para ti como para otros desarrolladores que puedan trabajar con tu código en el futuro. Python ofrece varias formas de documentar el código, desde simples comentarios hasta docstrings elaborados.

Comentarios en Python

Los comentarios son notas que se añaden al código para explicar su funcionamiento, pero que el intérprete de Python ignora durante la ejecución. Son útiles para aclarar secciones complejas, explicar decisiones de diseño o dejar notas para futuras revisiones.

Comentarios de una línea

En Python, los comentarios de una línea comienzan con el símbolo #:

# Este es un comentario de una línea
x = 5  # También puedes añadir comentarios al final de una línea de código

Comentarios multilínea

Python no tiene un formato específico para comentarios multilínea, pero puedes usar varias líneas con # o cadenas de texto que no se asignan a ninguna variable:

# Este es un comentario
# que abarca varias líneas
# utilizando el símbolo # en cada línea

"""
También puedes usar cadenas de texto de triple comilla
para crear bloques de texto que funcionen como comentarios.
Esto es útil para comentarios largos o documentación temporal.
"""

x = 10  # El código continúa aquí

Cuándo usar comentarios

Los comentarios deben usarse estratégicamente para mejorar la comprensión del código:

  1. Explicar código complejo: Cuando la lógica no es obvia a primera vista.
# Aplicamos el algoritmo de Fisher-Yates para mezclar la lista de manera eficiente
for i in range(len(lista) - 1, 0, -1):
    j = random.randint(0, i)
    lista[i], lista[j] = lista[j], lista[i]
  1. Documentar decisiones de diseño: Por qué se eligió una solución particular.
# Usamos un diccionario en lugar de una lista para búsquedas O(1)
usuarios = {}
  1. Marcar código temporal o problemas conocidos:
# TODO: Refactorizar esta función para mejorar el rendimiento
# FIXME: Esta función falla con valores negativos
# HACK: Solución temporal hasta que se actualice la API

Cuándo NO usar comentarios

Los comentarios no deben usarse para:

  1. Explicar código obvio: El código debe ser autoexplicativo cuando sea posible.
# Mal ejemplo
# Incrementa contador en 1
contador += 1

# Buen ejemplo (no necesita comentario)
contador += 1
  1. Mantener código obsoleto: Es mejor usar un sistema de control de versiones.
# No hagas esto
# función_antigua()
función_nueva()
  1. Compensar nombres poco descriptivos: Es mejor usar nombres claros.
# Mal ejemplo
x = y * 24 * 60 * 60  # Convertir días a segundos

# Mejor enfoque
segundos_por_dia = 24 * 60 * 60
segundos_totales = dias * segundos_por_dia

Docstrings: Documentación de código

Los docstrings (cadenas de documentación) son cadenas de texto especiales que se utilizan para documentar módulos, clases, métodos y funciones. A diferencia de los comentarios regulares, los docstrings son accesibles en tiempo de ejecución a través del atributo __doc__.

Formato básico de docstrings

Los docstrings se definen utilizando triples comillas (simples o dobles) al principio de un módulo, clase, método o función:

def calcular_area_circulo(radio):
    """
    Calcula el área de un círculo dado su radio.
    
    Args:
        radio (float): El radio del círculo.
        
    Returns:
        float: El área del círculo.
    """
    import math
    return math.pi * radio ** 2

# Acceder al docstring
print(calcular_area_circulo.__doc__)

Estilos de docstrings

Existen varios estilos de docstrings en Python. Los más comunes son:

1. Estilo Google

def dividir(a, b):
    """Divide dos números y devuelve el resultado.
    
    Args:
        a (int): El numerador.
        b (int): El denominador.
        
    Returns:
        float: El resultado de la división.
        
    Raises:
        ZeroDivisionError: Si b es cero.
        
    Examples:
        >>> dividir(10, 2)
        5.0
    """
    return a / b

2. Estilo reStructuredText (reST)

Este estilo es utilizado por Sphinx, una herramienta popular para generar documentación:

def dividir(a, b):
    """Divide dos números y devuelve el resultado.
    
    :param a: El numerador.
    :type a: int
    :param b: El denominador.
    :type b: int
    :return: El resultado de la división.
    :rtype: float
    :raises ZeroDivisionError: Si b es cero.
    
    .. code-block:: python
    
        >>> dividir(10, 2)
        5.0
    """
    return a / b

3. Estilo NumPy/SciPy

def dividir(a, b):
    """
    Divide dos números y devuelve el resultado.
    
    Parameters
    ----------
    a : int
        El numerador.
    b : int
        El denominador.
        
    Returns
    -------
    float
        El resultado de la división.
        
    Raises
    ------
    ZeroDivisionError
        Si b es cero.
        
    Examples
    --------
    >>> dividir(10, 2)
    5.0
    """
    return a / b

Docstrings para diferentes elementos

Docstring para módulos

Se coloca al principio del archivo:

"""
Módulo de utilidades matemáticas.

Este módulo proporciona funciones matemáticas básicas
como suma, resta, multiplicación y división.
"""

# Importaciones y código del módulo...

Docstring para clases

class Rectangulo:
    """
    Clase que representa un rectángulo.
    
    Esta clase permite crear rectángulos y calcular su área y perímetro.
    
    Attributes:
        ancho (float): El ancho del rectángulo.
        alto (float): El alto del rectángulo.
    """
    
    def __init__(self, ancho, alto):
        """
        Inicializa un nuevo rectángulo.
        
        Args:
            ancho (float): El ancho del rectángulo.
            alto (float): El alto del rectángulo.
        """
        self.ancho = ancho
        self.alto = alto

Docstring para métodos

def calcular_area(self):
    """
    Calcula el área del rectángulo.
    
    Returns:
        float: El área del rectángulo (ancho * alto).
    """
    return self.ancho * self.alto

Herramientas para documentación

Python ofrece varias herramientas integradas para trabajar con documentación:

La función help()

La función help() muestra la documentación de un objeto:

help(print)  # Muestra la documentación de la función print
help(str)    # Muestra la documentación de la clase str

El atributo doc

Puedes acceder directamente al docstring de un objeto mediante su atributo __doc__:

print(str.__doc__)  # Muestra el docstring de la clase str

Herramientas externas

Existen varias herramientas para generar documentación automática a partir de docstrings:

  1. Sphinx: Una herramienta popular para generar documentación en varios formatos (HTML, PDF, etc.).
  2. pydoc: Una herramienta incluida en Python para generar documentación.
  3. MkDocs: Una herramienta para generar documentación en formato Markdown.
  4. pdoc: Una alternativa simple a pydoc.

Buenas prácticas de documentación

1. Sé consistente

Elige un estilo de docstring y úsalo de manera consistente en todo tu proyecto.

2. Documenta mientras programas

Es más fácil documentar el código mientras lo escribes que hacerlo después.

3. Actualiza la documentación

Mantén la documentación actualizada cuando cambies el código.

4. Documenta el "qué" y el "por qué", no el "cómo"

El código muestra cómo se hace algo. La documentación debe explicar qué hace y por qué.

# Mal ejemplo
# Este bucle recorre la lista y suma cada elemento
for item in lista:
    total += item

# Buen ejemplo
# Calculamos la suma para determinar el promedio posteriormente
for item in lista:
    total += item

5. Usa ejemplos

Los ejemplos son una forma efectiva de mostrar cómo usar tu código:

def fahrenheit_a_celsius(fahrenheit):
    """
    Convierte grados Fahrenheit a Celsius.
    
    Args:
        fahrenheit (float): Temperatura en grados Fahrenheit.
        
    Returns:
        float: Temperatura en grados Celsius.
        
    Examples:
        >>> fahrenheit_a_celsius(32)
        0.0
        >>> fahrenheit_a_celsius(212)
        100.0
    """
    return (fahrenheit - 32) * 5 / 9

6. Incluye información sobre excepciones

Documenta las excepciones que tu función puede lanzar:

def obtener_elemento(lista, indice):
    """
    Obtiene un elemento de la lista en el índice especificado.
    
    Args:
        lista (list): La lista de elementos.
        indice (int): El índice del elemento a obtener.
        
    Returns:
        any: El elemento en el índice especificado.
        
    Raises:
        IndexError: Si el índice está fuera del rango de la lista.
        TypeError: Si el índice no es un entero.
    """
    return lista[indice]

Comentarios especiales

Python y muchas herramientas de desarrollo reconocen ciertos comentarios especiales:

TODO

Indica tareas pendientes:

# TODO: Implementar validación de entrada
# TODO(usuario): Optimizar algoritmo de búsqueda

FIXME

Indica problemas conocidos que deben solucionarse:

# FIXME: Esta función falla con valores negativos

HACK

Indica soluciones temporales o no ideales:

# HACK: Solución temporal hasta que se actualice la API

NOTE

Proporciona información adicional importante:

# NOTE: Esta función asume que la lista está ordenada

Documentación de proyectos

Además de documentar el código, es importante documentar el proyecto en su conjunto:

README

El archivo README es generalmente el primer documento que ven los usuarios:

# Mi Proyecto

Una breve descripción de lo que hace mi proyecto.

## Instalación

```bash
pip install mi-proyecto

Uso

import mi_proyecto

mi_proyecto.funcion_principal()

Características

  • Característica 1
  • Característica 2

Licencia

MIT


### Documentación de API

Para proyectos más grandes, es útil tener documentación específica de la API:

```markdown
# API Reference

## `module.function(param1, param2)`

Description of what the function does.

### Parameters:

- `param1` (type): Description of param1.
- `param2` (type): Description of param2.

### Returns:

- (return_type): Description of return value.

### Example:

```python
result = module.function("value1", "value2")

## Ejemplos prácticos

### Ejemplo 1: Función bien documentada

```python
def calcular_estadisticas(numeros):
    """
    Calcula estadísticas básicas para una lista de números.
    
    Esta función toma una lista de números y calcula varias
    estadísticas descriptivas: mínimo, máximo, suma, promedio,
    y desviación estándar.
    
    Args:
        numeros (list): Una lista de números (int o float).
        
    Returns:
        dict: Un diccionario con las estadísticas calculadas:
            - 'min': El valor mínimo
            - 'max': El valor máximo
            - 'suma': La suma de todos los valores
            - 'promedio': El promedio de los valores
            - 'desviacion': La desviación estándar
            
    Raises:
        ValueError: Si la lista está vacía.
        TypeError: Si la lista contiene elementos no numéricos.
        
    Examples:
        >>> calcular_estadisticas([1, 2, 3, 4, 5])
        {'min': 1, 'max': 5, 'suma': 15, 'promedio': 3.0, 'desviacion': 1.4142}
    """
    if not numeros:
        raise ValueError("La lista no puede estar vacía")
    
    # Verificar que todos los elementos son numéricos
    if not all(isinstance(n, (int, float)) for n in numeros):
        raise TypeError("Todos los elementos deben ser numéricos")
    
    # Calcular estadísticas
    minimo = min(numeros)
    maximo = max(numeros)
    suma = sum(numeros)
    promedio = suma / len(numeros)
    
    # Calcular desviación estándar
    suma_cuadrados = sum((x - promedio) ** 2 for x in numeros)
    desviacion = (suma_cuadrados / len(numeros)) ** 0.5
    
    return {
        'min': minimo,
        'max': maximo,
        'suma': suma,
        'promedio': promedio,
        'desviacion': round(desviacion, 4)
    }

Ejemplo 2: Clase bien documentada

class CuentaBancaria:
    """
    Clase que representa una cuenta bancaria simple.
    
    Esta clase permite crear cuentas bancarias y realizar operaciones
    básicas como depósitos, retiros y consultas de saldo.
    
    Attributes:
        titular (str): Nombre del titular de la cuenta.
        saldo (float): Saldo actual de la cuenta.
        numero_cuenta (str): Número único de la cuenta.
        activa (bool): Indica si la cuenta está activa.
    """
    
    def __init__(self, titular, saldo_inicial=0):
        """
        Inicializa una nueva cuenta bancaria.
        
        Args:
            titular (str): Nombre del titular de la cuenta.
            saldo_inicial (float, optional): Saldo inicial de la cuenta. 
                                            Por defecto es 0.
        
        Raises:
            ValueError: Si el saldo inicial es negativo.
            TypeError: Si el titular no es una cadena de texto.
        """
        if not isinstance(titular, str):
            raise TypeError("El titular debe ser una cadena de texto")
        
        if saldo_inicial < 0:
            raise ValueError("El saldo inicial no puede ser negativo")
        
        self.titular = titular
        self.saldo = saldo_inicial
        self.numero_cuenta = self._generar_numero_cuenta()
        self.activa = True
    
    def _generar_numero_cuenta(self):
        """
        Genera un número de cuenta aleatorio.
        
        Este es un método privado utilizado internamente.
        
        Returns:
            str: Un número de cuenta de 10 dígitos.
        """
        import random
        return ''.join(str(random.randint(0, 9)) for _ in range(10))
    
    def depositar(self, cantidad):
        """
        Deposita una cantidad en la cuenta.
        
        Args:
            cantidad (float): La cantidad a depositar.
            
        Returns:
            float: El nuevo saldo de la cuenta.
            
        Raises:
            ValueError: Si la cantidad es negativa o cero.
            RuntimeError: Si la cuenta no está activa.
        """
        if not self.activa:
            raise RuntimeError("No se puede depositar en una cuenta inactiva")
        
        if cantidad <= 0:
            raise ValueError("La cantidad a depositar debe ser positiva")
        
        self.saldo += cantidad
        return self.saldo
    
    def retirar(self, cantidad):
        """
        Retira una cantidad de la cuenta.
        
        Args:
            cantidad (float): La cantidad a retirar.
            
        Returns:
            float: El nuevo saldo de la cuenta.
            
        Raises:
            ValueError: Si la cantidad es negativa, cero o mayor que el saldo.
            RuntimeError: Si la cuenta no está activa.
        """
        if not self.activa:
            raise RuntimeError("No se puede retirar de una cuenta inactiva")
        
        if cantidad <= 0:
            raise ValueError("La cantidad a retirar debe ser positiva")
        
        if cantidad > self.saldo:
            raise ValueError("Fondos insuficientes")
        
        self.saldo -= cantidad
        return self.saldo
    
    def consultar_saldo(self):
        """
        Consulta el saldo actual de la cuenta.
        
        Returns:
            float: El saldo actual de la cuenta.
            
        Raises:
            RuntimeError: Si la cuenta no está activa.
        """
        if not self.activa:
            raise RuntimeError("No se puede consultar una cuenta inactiva")
        
        return self.saldo
    
    def cerrar_cuenta(self):
        """
        Cierra la cuenta bancaria.
        
        Una cuenta cerrada no permite realizar operaciones.
        
        Returns:
            bool: True si la cuenta se cerró correctamente.
            
        Raises:
            RuntimeError: Si la cuenta ya está inactiva.
        """
        if not self.activa:
            raise RuntimeError("La cuenta ya está inactiva")
        
        self.activa = False
        return True
    
    def __str__(self):
        """
        Devuelve una representación en cadena de la cuenta.
        
        Returns:
            str: Una cadena con información de la cuenta.
        """
        estado = "activa" if self.activa else "inactiva"
        return f"Cuenta {self.numero_cuenta} de {self.titular}: ${self.saldo:.2f} ({estado})"

Ejercicios prácticos

  1. Documenta una función que calcule el factorial de un número usando docstrings.
  2. Añade comentarios apropiados a un algoritmo de ordenamiento (como bubble sort o quicksort).
  3. Crea una clase Libro con atributos y métodos, y documéntala completamente usando el estilo de docstrings que prefieras.
  4. Escribe un módulo simple con varias funciones y añade documentación a nivel de módulo y de función.
  5. Revisa un proyecto existente y mejora su documentación siguiendo las buenas prácticas aprendidas.

En la próxima lección, aprenderemos sobre estructuras de control condicionales en Python, que nos permitirán tomar decisiones en nuestros programas.

Entrada y Salida en Python
Aprende a interactuar con el usuario mediante la entrada y s...
Condicionales
Aprende a controlar el flujo de ejecución de tus programas P...
Referencias
Python.org. PEP 257 -- Docstring Conventions. https://www.python.org/dev/peps/pep-0257/
Real Python. Documenting Python Code: A Complete Guide. https://realpython.com/documenting-python-code/

Conceptos Básicos de HTML

Aprende los conceptos básicos de HTML

Conceptos Básicos de CSS

Aprende los conceptos básicos de CSS

Conceptos Básicos de JavaScript

Aprende los conceptos básicos de JavaScript

Conceptos Básicos SQL

Aprende los conceptos básicos de SQL

Conceptos Básicos de GIT

Aprende los conceptos básicos de GIT

Conceptos Básicos de UML

Aprende los conceptos básicos de UML

Refuerzo Academico de Herramientas de Productividad 2025