Saltar al contenido principal
Intermedio1 hora
Prerequisitos: Type Hints · Funciones
Módulo 05: Calidad y RobustezLección 4 de 4
100% completado

Calidad de Código en Python

🗺️ Mapa de la Lección

1 hora🟡 Intermedio

🎯 Qué aprenderás

  • Usar formateadores (black) para código consistente
  • Usar linters (flake8) para encontrar problemas
  • Usar type checkers (mypy) para verificar tipos
  • Configurar pre-commit hooks
  • Aplicar estándares de calidad en proyectos

🌍 Para qué te sirve

  • Colaboración: mantener código consistente en equipos
  • Mantenimiento: encontrar errores antes de ejecutar
  • Profesionalismo: código que sigue estándares
  • CI/CD: verificar calidad automáticamente
  • Refactoring: herramientas ayudan a hacer cambios seguros
  • Onboarding: nuevos desarrolladores entienden el código más rápido

🎯 ¿Por qué aprender herramientas de calidad de código?

Escribir código que funciona es solo la mitad del trabajo. El código también debe ser legible, mantenible, consistente y sin errores obvios.

Las herramientas de calidad son esenciales porque:

  • Colaboración: otros desarrolladores pueden trabajar contigo fácilmente
  • Mantenimiento: es más fácil arreglar bugs y agregar features
  • Profesionalismo: código de calidad es señal de experiencia
  • Ahorro de tiempo: encuentras errores antes, no después de deploy
  • Consistencia: todo el equipo sigue las mismas convenciones

🌍 Casos reales donde se usa

Las herramientas de calidad están en todos los proyectos profesionales:

  • Colaboración: Mantener código consistente en equipos
  • CI/CD: Verificar calidad automáticamente en pipelines
  • Mantenimiento: Encontrar errores antes de ejecutar
  • Refactoring: Herramientas ayudan a hacer cambios seguros
  • Onboarding: Nuevos desarrolladores entienden el código más rápido
  • Profesionalismo: Código que sigue estándares de la industria

Ejemplo real: En proyectos open source como Django o Flask, todas las contribuciones pasan por verificaciones automáticas de calidad antes de ser aceptadas.

💡 Concepto base

Las herramientas de calidad de código verifican automáticamente que tu código siga estándares, esté bien formateado, y no tenga errores obvios.

Lo genial de Python: Hay herramientas excelentes y gratuitas que puedes integrar fácilmente en tu flujo de trabajo.

# Código sin formatear (funciona, pero inconsistente)
def calcular_total(precio,cantidad):
return precio*cantidad

# Después de black (formateado automáticamente)
def calcular_total(precio, cantidad):
return precio * cantidad

Este capítulo cubre:

  • Formateadores (black): Formato automático consistente
  • Linters (flake8): Encuentran problemas de estilo y errores comunes
  • Type checkers (mypy): Verifican tipos antes de ejecutar
  • Pre-commit hooks: Verificaciones automáticas antes de hacer commit
Resultado esperado:
# Black formatea automáticamente:
# - Espacios alrededor de operadores
# - Líneas de máximo 88 caracteres
# - Indentación consistente
# - Y más...
🌮 Analogía culinaria

Las herramientas de calidad de código son como los estándares de higiene y presentación en una cocina profesional. Un formateador (black) es como tener un estándar de cómo cortar las tortillas: todas del mismo tamaño y forma. Un linter (flake8) es como un inspector que revisa que uses ingredientes frescos y sigas las medidas correctas. Un type checker (mypy) es como verificar que estés usando los ingredientes correctos antes de cocinar. Los pre-commit hooks son como una revisión final antes de servir el plato, asegurando que todo esté perfecto. Cada herramienta tiene su rol en mantener la calidad.

Para principiantes

Antes de continuar: Asegúrate de entender Type Hints y Funciones. Estas herramientas no cambian cómo funciona tu código, solo verifican que siga buenas prácticas y estándares.

Herramientas de Análisis

black (Formateador)

# pyproject.toml
[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''

flake8 (Linter)

# setup.cfg
[flake8]
max-line-length = 88
extend-ignore = E203
exclude = .git,__pycache__,build,dist
per-file-ignores =
__init__.py: F401
tests/*: S101

mypy (Type Checker)

# mypy.ini
[mypy]
python_version = 3.8
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True

[mypy.plugins.pandas.*]
ignore_missing_imports = True

Buenas Prácticas

Estilo de Código

# ❌ Mal
def calcular_promedio(x,y):
return sum(x)/len(x)

# ✅ Bien
def calcular_promedio(numeros: list[float]) -> float:
"""
Calcula el promedio de una lista de números.

Args:
numeros: Lista de números a promediar

Returns:
El promedio de los números

Raises:
ValueError: Si la lista está vacía
"""
if not numeros:
raise ValueError("La lista no puede estar vacía")
return sum(numeros) / len(numeros)

Documentación

class Usuario:
"""
Representa un usuario del sistema.

Attributes:
nombre: Nombre completo del usuario
email: Correo electrónico del usuario
activo: Estado de la cuenta del usuario
"""

def __init__(self, nombre: str, email: str) -> None:
"""
Inicializa un nuevo usuario.

Args:
nombre: Nombre completo del usuario
email: Correo electrónico del usuario
"""
self.nombre = nombre
self.email = email
self.activo = True

Manejo de Errores

def procesar_archivo(ruta: str) -> dict:
"""
Procesa un archivo y retorna su contenido como diccionario.

Args:
ruta: Ruta al archivo a procesar

Returns:
Diccionario con el contenido procesado

Raises:
FileNotFoundError: Si el archivo no existe
ValueError: Si el archivo está mal formateado
"""
try:
with open(ruta, 'r') as f:
contenido = f.read()
except FileNotFoundError:
raise FileNotFoundError(f"Archivo no encontrado: {ruta}")

try:
return json.loads(contenido)
except json.JSONDecodeError:
raise ValueError(f"Archivo mal formateado: {ruta}")

Herramientas de Desarrollo

pre-commit

# .pre-commit-config.yaml
repos:
- repo: https://github.com/psf/black
rev: 21.12b0
hooks:
- id: black
language_version: python3.8

- repo: https://github.com/pycqa/flake8
rev: 4.0.1
hooks:
- id: flake8
additional_dependencies: [flake8-docstrings]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.910
hooks:
- id: mypy
additional_dependencies: [types-requests]

pytest

# pytest.ini
[pytest]
testpaths = tests
python_files = test_*.py
python_functions = test_*
addopts = -v --cov=mi_proyecto --cov-report=term-missing

Ejemplos de Código de Calidad

Clase Bien Estructurada

from dataclasses import dataclass
from typing import Optional
from datetime import datetime

@dataclass
class Transaccion:
"""
Representa una transacción financiera.

Attributes:
id: Identificador único de la transacción
monto: Monto de la transacción
fecha: Fecha y hora de la transacción
descripcion: Descripción opcional de la transacción
"""
id: int
monto: float
fecha: datetime
descripcion: Optional[str] = None

def es_valida(self) -> bool:
"""
Verifica si la transacción es válida.

Returns:
True si la transacción es válida, False en caso contrario
"""
return (
self.id > 0 and
self.monto != 0 and
self.fecha <= datetime.now()
)

def formatear_monto(self) -> str:
"""
Formatea el monto de la transacción.

Returns:
String con el monto formateado
"""
return f"${self.monto:,.2f}"

Función con Manejo de Errores

from typing import List, Dict, Any
import logging

logger = logging.getLogger(__name__)

def procesar_datos(
datos: List[Dict[str, Any]],
config: Dict[str, Any]
) -> List[Dict[str, Any]]:
"""
Procesa una lista de datos según la configuración.

Args:
datos: Lista de diccionarios con datos a procesar
config: Diccionario con configuración de procesamiento

Returns:
Lista de diccionarios con datos procesados

Raises:
ValueError: Si los datos o la configuración son inválidos
ProcessingError: Si hay un error durante el procesamiento
"""
if not datos:
logger.warning("Lista de datos vacía")
return []

if not config:
raise ValueError("Configuración requerida")

try:
resultados = []
for item in datos:
resultado = procesar_item(item, config)
resultados.append(resultado)
return resultados

except Exception as e:
logger.error(f"Error procesando datos: {e}")
raise ProcessingError(f"Error en procesamiento: {e}")

Consejos

  1. Sigue las guías de estilo (PEP 8)
  2. Escribe documentación clara
  3. Usa type hints
  4. Escribe pruebas unitarias
  5. Mantén el código simple y legible

Recursos Adicionales

Documentación Oficial

Bibliografía Recomendada

  • Clean Code (Robert C. Martin) - Principios fundamentales de código limpio
  • The Clean Coder (Robert C. Martin) - Profesionalismo en programación
  • Refactoring (Martin Fowler) - Técnicas para mejorar código existente
  • Effective Python (Brett Slatkin) - 59 formas de escribir mejor Python
  • Python Tricks (Dan Bader) - Trucos y mejores prácticas
  • Code Complete (Steve McConnell) - Construcción de software profesional

Conceptos Relacionados


Siguiente paso

Ahora que conoces las herramientas de calidad, aprende a trabajar con archivos y formatos de datos. Continúa con: Pathlib