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
- Buenas prácticas
- Sé claro y conciso en tus comentarios
- Explicar el "por qué", no el "qué"
- Actualiza los comentarios cuando cambies el código
- Usa comentarios TODO para tareas pendientes
- Incluir enlaces a documentación externa
- Documenta código complejo y algoritmos
- Evita comentarios obvios o redundantes
- Usa comentarios para explicar decisiones de diseño
- Comenta las excepciones y casos edge
- Espaciado en comentarios
- Formato
- 1 línea
- Multilínea
- Docstrings
- Docstrings de funciones
- Docstrings de generadores
- Docstrings de clases
- Docstrings de módulos
- Métodos sobrescritos con @override
- Docstrings de propiedades
- Reglas adicionales a tener en cuenta
- Límite de línea en docstrings
- No uses docstrings triviales en tests
- Consistencia en el estilo
- Conclusiones
- 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