Guía rápida para escribir buenos comentarios en Python

Los comentarios son la mitad de tu código. Son tan esenciales, que sin ellos pueden hacer un código incomprensible, y por lo tanto hacer que un proyecto sea imposible de mantener. Puedes explicar decisiones de diseño, algoritmos complejos y destacan casos concreto muy importantes en el desarrollo. Por otro lado, usando docstrings, hasta puedes autogenerar documentación del proyecto.

En este artículo me gustaría explicar las mejores prácticas para escribir comentarios y docstrings en Python, así como las convenciones más básicas que sería muy recomendable que siguieras.

Buenas prácticas

Sé claro y conciso en tus comentarios

Explique el propósito del código, no describas lo que ya es obvio.

# 👍 Bien
# Calcular el impuesto del 21%
tax = price * 0.21

# 👎 Mal
# Multiplicar el precio por 0.21
tax = price * 0.21

Explicar el "por qué", no el "qué"

# 👍 Bien
# Usa la búsqueda binaria para mejorar el rendimiento con grandes datasets
result = binary_search(data, target)

# 👎 Mal
# Llama a la función de búsqueda binaria
result = binary_search(data, target)

Actualiza los comentarios cuando cambies el código

# 👎 Mal
# Calcula descuento (10%)
discount = price * 0.15

Usa comentarios TODO para tareas pendientes

# 👍 Bien
def update_password(input):
    # TODO: crbug.com/192795 - Comprobar con el diccionario de contraseñas comunes
    # TODO: Añadir validación de longitud mínima

Incluir enlaces a documentación externa

# 👍 Bien
def check_email(input):
    # Regular expression: https://stackoverflow.com/questions/46155/how-can-i-validate-an-email-address-in-javascript
    pattern = re.compile(r'^(([^<>()[\]\\.,;:\s@"]+(...')
    return bool(pattern.match(input))

Documenta código complejo y algoritmos

# 👍 Bien
# Usamos una búsqueda binaria ponderada para encontrar la posición de i
# en el array. Extrapolamos la posición basándonos en el número más grande
# del array y el tamaño del array, luego hacemos búsqueda binaria para
# obtener el número exacto.
if i & (i-1) == 0:  # Verdadero si i es 0 o una potencia de 2
    binary_search_weighted(array, i)

Evita comentarios obvios o redundantes

# 👎 Mal - comentario innecesario
x = x + 1  # Incrementa x en 1

# 👍 Bien - sin comentario porque es obvio
x = x + 1

Usa comentarios para explicar decisiones de diseño

# 👍 Bien
# Usamos un diccionario en lugar de una lista porque
# porque esperamos expandir la estructura en el futuro
user_cache = {}

Comenta las excepciones y casos edge

# 👍 Bien
def divide(a, b):
    if b == 0:
        # Evitamos división por cero devolviendo None en lugar de lanzar excepción
        # para mantener compatibilidad con versiones anteriores
        return None
    return a / b

Espaciado en comentarios

Los comentarios deben comenzar al menos a 2 espacios del código:

# 👍 Bien
if i & (i-1) == 0:  # Verdadero si i es 0 o una potencia de 2

# 👎 Mal
if i & (i-1) == 0:# Sin espacios antes del comentario

Formato

1 línea

# Puede ir encima
x = 5  # O al final de la línea

Multilínea

# Esto es un comentario
# con múltiples líneas
# para explicar algo complejo

"""
También puedes usar comillas triples
pero es mejor reservarlas para
docstrings
"""

Docstrings

Para documentar funciones, clases y módulos, usa docstrings.

Existen 3 formatos de docstrings: Google, NumPy y Sphinx/reStructuredText. El primero es el más fácil de leer y entender, por lo que se recomienda.

Docstrings de funciones

def create_account(username: str, email: str, age: int) -> bool:
    """Create account with username and email.

    Creates a new user account with the provided credentials and validates
    the user's age for compliance with service requirements.

    Args:
        username: Username for the account. Must be unique and contain
            only alphanumeric characters and underscores.
        email: Email address for the account. Must be a valid email format
            and will be used for account verification.
        age: Age of the user in years. Must be 13 or older to create
            an account.

    Returns:
        True if the account was created successfully, False otherwise.

    Raises:
        ValueError: If username contains invalid characters or if age
            is below the minimum required age.
        EmailError: If the email format is invalid or already exists
            in the system.

    Example:
        >>> create_account("john_doe", "john@example.com", 25)
        True
        >>> create_account("jane_smith", "jane@test.org", 30)
        True
    """
    # Aquí iría la lógica de creación de cuenta
    return True

Docstrings de generadores

Usa Yields en lugar de Returns para funciones generadoras:

def fibonacci_generator(n: int):
    """Generate the first n Fibonacci numbers.

    Args:
        n: Number of Fibonacci numbers to generate.

    Yields:
        int: The next Fibonacci number in the sequence.

    Example:
        >>> list(fibonacci_generator(5))
        [0, 1, 1, 2, 3]
    """
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

Docstrings de clases

class DatabaseManager:
    """Manages database connections and operations.

    This class handles all database interactions including connection
    pooling, query execution, and transaction management.

    Attributes:
        connection_string: Database connection string.
        max_connections: Maximum number of concurrent connections allowed.
        timeout: Connection timeout in seconds.

    Example:
        >>> db = DatabaseManager("postgresql://localhost/mydb")
        >>> db.connect()
        >>> results = db.execute_query("SELECT * FROM users")
    """

    def __init__(self, connection_string: str, max_connections: int = 10):
        """Initialize the database manager.

        Args:
            connection_string: Database connection string.
            max_connections: Maximum number of concurrent connections.
        """
        self.connection_string = connection_string
        self.max_connections = max_connections

Docstrings de módulos

"""Database utilities for user management.

This module provides utilities for managing user accounts in the database,
including creation, deletion, authentication, and profile management.

Example usage:
    from user_db import create_user, authenticate_user

    create_user("john_doe", "john@example.com")
    is_valid = authenticate_user("john_doe", "password123")
"""

import hashlib
import sqlite3

Métodos sobrescritos con @override

from typing_extensions import override

class EmailService:
    def send_message(self, message: str) -> bool:
        """Send a message via email."""
        # Implementación base
        pass

class SMSService(EmailService):
    @override
    def send_message(self, message: str) -> bool:
        # No necesita docstring completo si solo cambia el comportamiento interno
        # pero mantiene el mismo contrato
        pass

    @override
    def send_message(self, message: str) -> bool:
        """Send a message via SMS.

        Overrides the base email implementation to send via SMS instead.
        Message length is limited to 160 characters.

        Args:
            message: Message to send. Will be truncated if longer than 160 chars.

        Returns:
            True if message was sent successfully.

        Raises:
            ValueError: If message is empty.
        """
        if not message:
            raise ValueError("Message cannot be empty")
        return self._send_sms(message[:160])

Docstrings de propiedades

class Circle:
    def __init__(self, radius: float):
        self._radius = radius

    @property
    def radius(self) -> float:
        """The radius of the circle."""
        return self._radius

    @property
    def area(self) -> float:
        """The area of the circle."""
        return 3.14159 * self._radius ** 2

Reglas adicionales a tener en cuenta

Límite de línea en docstrings

Las líneas de resumen de docstrings deben mantenerse dentro del límite de 80 caracteres.

No uses docstrings triviales en tests

# 👎 Mal
def test_user_creation():
    """Tests for user creation."""
    pass

# 👍 Bien - solo si aporta información nueva
def test_user_creation():
    """Test user creation with edge cases for special characters in usernames."""
    pass

# 👍 Mejor - sin docstring innecesario
def test_user_creation():
    pass

Consistencia en el estilo

Mantén consistencia entre estilo descriptivo e imperativo dentro del mismo archivo:

# Estilo imperativo (recomendado)
def fetch_user_data():
    """Fetch user data from the database."""
    pass

def update_user_profile():
    """Update user profile information."""
    pass

# O estilo descriptivo (pero sé consistente)
def fetch_user_data():
    """Fetches user data from the database."""
    pass

def update_user_profile():
    """Updates user profile information."""
    pass

Conclusiones

Escribir buenos comentarios habla muy bien de un desarrollador. Además creas un código que será fácil de pasar a otros desarrolladores o a ti mismo en el futuro (la memoria es frágil). Incluye los patrones, como los docstrings, en tu flujo de trabajo diario y el proyecto se beneficiará enormemente.

Y que no te gane la pereza: mantén la constancia y consistencia.

Bibliografía

Este trabajo está bajo una licencia Attribution-NonCommercial-NoDerivatives 4.0 International.

¿Me invitas a un café?

Puedes usar el terminal.

ssh customer@andros.dev -p 5555

Escrito por Andros Fenollosa

agosto 7, 2025

6 min de lectura

Sigue leyendo