Communitygithub.com

patrones-python

Modismos pitónicos, estándares PEP 8, sugerencias de tipo y mejores prácticas para construir aplicaciones Python robustas, eficientes y mantenibles. Úsalo al escribir o revisar código Python y cuando haya dudas sobre estructura idiomática, tipado o PEP 8.

¿Qué es patrones-python?

Esta habilidad proporciona una guía completa de patrones y mejores prácticas de desarrollo Python, cubriendo desde principios fundamentales como legibilidad, explicitación y EAFP, hasta temas avanzados como sugerencias de tipo, manejo de errores, administradores de contexto, comprensiones, generadores, clases de datos, decoradores y concurrencia. Es útil para desarrolladores que escriben, revisan o refactorizan código Python, ya que ofrece ejemplos concretos de código correcto e incorrecto, alineados con los estándares PEP 8 y el Zen de Python. La habilidad se puede activar en cualquier proyecto Python, independientemente del marco o la plataforma (CLI, web, ciencia de datos, etc.), y promueve un estilo de código mantenible y eficiente mediante sugerencias de tipo modernas, manejo de excepciones específico y patrones de concurrencia apropiados. Incluye también integración con herramientas como Black, isort y Ruff para formateo y linting automáticos.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/python-patterns

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones de Desarrollo Python

Patrones y mejores prácticas pitónicos para construir aplicaciones robustas, eficientes y mantenibles.

Cuándo Activar

  • Escribir nuevo código Python
  • Revisar código Python
  • Refactorizar código Python existente
  • Diseñar paquetes/módulos Python

Principios Fundamentales

1. La Legibilidad Cuenta

Python prioriza la legibilidad. El código debe ser obvio y fácil de entender.

# Bueno: Claro y legible
def obtener_usuarios_activos(usuarios: list[Usuario]) -> list[Usuario]:
    """Devuelve solo los usuarios activos de la lista proporcionada."""
    return [usuario for usuario in usuarios if usuario.activo]


# Malo: Ingenioso pero confuso
def obtener_usuarios_activos(u):
    return [x for x in u if x.a]

2. Explícito es Mejor que Implícito

Evita la magia; sé claro sobre lo que hace tu código.

# Bueno: Configuración explícita
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

# Malo: Efectos secundarios ocultos
import algun_modulo
algun_modulo.configurar()  # ¿Qué hace esto?

3. EAFP - Es Más Fácil Pedir Perdón que Permiso

Python prefiere el manejo de excepciones sobre la comprobación de condiciones.

# Bueno: Estilo EAFP
def obtener_valor(diccionario: dict, clave: str, valor_por_defecto: Cualquiera = None) -> Cualquiera:
    try:
        return diccionario[clave]
    except KeyError:
        return valor_por_defecto

# Malo: Estilo LBYL (Mira Antes de Saltar)
def obtener_valor(diccionario: dict, clave: str, valor_por_defecto: Cualquiera = None) -> Cualquiera:
    if clave in diccionario:
        return diccionario[clave]
    else:
        return valor_por_defecto

Sugerencias de Tipo

Anotaciones de Tipo Básicas

from typing import Optional, List, Dict, Any

def procesar_usuario(
    id_usuario: str,
    datos: Dict[str, Any],
    activo: bool = True
) -> Optional[Usuario]:
    """Procesa un usuario y devuelve el Usuario actualizado o None."""
    if not activo:
        return None
    return Usuario(id_usuario, datos)

Sugerencias de Tipo Modernas (Python 3.9+)

# Python 3.9+ - Usa tipos incorporados
def procesar_elementos(elementos: list[str]) -> dict[str, int]:
    return {elemento: len(elemento) for elemento in elementos}

# Python 3.8 y anteriores - Usa el módulo typing
from typing import List, Dict

def procesar_elementos(elementos: List[str]) -> Dict[str, int]:
    return {elemento: len(elemento) for elemento in elementos}

Alias de Tipo y TypeVar

from typing import TypeVar, Union

# Alias de tipo para tipos complejos
JSON = Union[dict[str, Any], list[Any], str, int, float, bool, None]

def analizar_json(datos: str) -> JSON:
    return json.loads(datos)

# Tipos genéricos
T = TypeVar('T')

def primero(elementos: list[T]) -> T | None:
    """Devuelve el primer elemento o None si la lista está vacía."""
    return elementos[0] if elementos else None

Tipado Dinámico Basado en Protocolos

from typing import Protocol

class Renderizable(Protocol):
    def renderizar(self) -> str:
        """Renderiza el objeto a una cadena."""

def renderizar_todos(elementos: list[Renderizable]) -> str:
    """Renderiza todos los elementos que implementan el protocolo Renderizable."""
    return "\n".join(elemento.renderizar() for elemento in elementos)

Patrones de Manejo de Errores

Manejo Específico de Excepciones

# Bueno: Captura excepciones específicas
def cargar_configuracion(ruta: str) -> Configuracion:
    try:
        with open(ruta) as f:
            return Configuracion.desde_json(f.read())
    except FileNotFoundError as e:
        raise ErrorConfiguracion(f"Archivo de configuración no encontrado: {ruta}") from e
    except json.JSONDecodeError as e:
        raise ErrorConfiguracion(f"JSON inválido en configuración: {ruta}") from e

# Malo: Excepto desnudo
def cargar_configuracion(ruta: str) -> Configuracion:
    try:
        with open(ruta) as f:
            return Configuracion.desde_json(f.read())
    except:
        return None  # ¡Fallo silencioso!

Encadenamiento de Excepciones

def procesar_datos(datos: str) -> Resultado:
    try:
        analizado = json.loads(datos)
    except json.JSONDecodeError as e:
        # Encadenar excepciones para preservar el rastreo
        raise ValueError(f"Error al analizar datos: {datos}") from e

Jerarquía de Excepciones Personalizadas

class ErrorApp(Exception):
    """Excepción base para todos los errores de la aplicación."""
    pass

class ErrorValidacion(ErrorApp):
    """Se lanza cuando falla la validación de entrada."""
    pass

class ErrorNoEncontrado(ErrorApp):
    """Se lanza cuando no se encuentra un recurso solicitado."""
    pass

# Uso
def obtener_usuario(id_usuario: str) -> Usuario:
    usuario = db.encontrar_usuario(id_usuario)
    if not usuario:
        raise ErrorNoEncontrado(f"Usuario no encontrado: {id_usuario}")
    return usuario

Administradores de Contexto

Gestión de Recursos

# Bueno: Usando administradores de contexto
def procesar_archivo(ruta: str) -> str:
    with open(ruta, 'r') as f:
        return f.read()

# Malo: Gestión manual de recursos
def procesar_archivo(ruta: str) -> str:
    f = open(ruta, 'r')
    try:
        return f.read()
    finally:
        f.close()

Administradores de Contexto Personalizados

from contextlib import contextmanager

@contextmanager
def temporizador(nombre: str):
    """Administrador de contexto para medir el tiempo de un bloque de código."""
    inicio = time.perf_counter()
    yield
    elapsed = time.perf_counter() - inicio
    print(f"{nombre} tomó {elapsed:.4f} segundos")

# Uso
with temporizador("procesamiento de datos"):
    procesar_conjunto_grande_de_datos()

Clases de Administradores de Contexto

class TransaccionBaseDatos:
    def __init__(self, conexion):
        self.conexion = conexion

    def __enter__(self):
        self.conexion.iniciar_transaccion()
        return self

    def __exit__(self, tipo_exc, val_exc, tb_exc):
        if tipo_exc is None:
            self.conexion.confirmar()
        else:
            self.conexion.revertir()
        return False  # No suprimir excepciones

# Uso
with TransaccionBaseDatos(conn):
    usuario = conn.crear_usuario(datos_usuario)
    conn.crear_perfil(usuario.id, datos_perfil)

Comprensiones y Generadores

Comprensiones de Lista

# Bueno: Comprensión de lista para transformaciones simples
nombres = [usuario.nombre for usuario in usuarios if usuario.activo]

# Malo: Bucle manual
nombres = []
for usuario in usuarios:
    if usuario.activo:
        nombres.append(usuario.nombre)

# Las comprensiones complejas deben expandirse
# Malo: Demasiado complejo
resultado = [x * 2 for x in elementos if x > 0 if x % 2 == 0]

# Bueno: Usar una función generadora
def filtrar_y_transformar(elementos: Iterable[int]) -> list[int]:
    resultado = []
    for x in elementos:
        if x > 0 and x % 2 == 0:
            resultado.append(x * 2)
    return resultado

Expresiones Generadoras

# Bueno: Generador para evaluación perezosa
total = sum(x * x for x in range(1_000_000))

# Malo: Crea una lista intermedia grande
total = sum([x * x for x in range(1_000_000)])

Funciones Generadoras

def leer_archivo_grande(ruta: str) -> Iterator[str]:
    """Lee un archivo grande línea por línea."""
    with open(ruta) as f:
        for linea in f:
            yield linea.strip()

# Uso
for linea in leer_archivo_grande("enorme.txt"):
    procesar(linea)

Clases de Datos y Tuplas Nombradas

Clases de Datos

from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class Usuario:
    """Entidad de usuario con __init__, __repr__ y __eq__ automáticos."""
    id: str
    nombre: str
    email: str
    creado_en: datetime = field(default_factory=datetime.now)
    activo: bool = True

# Uso
usuario = Usuario(
    id="123",
    nombre="Alicia",
    email="[email protected]"
)

Clases de Datos con Validación

@dataclass
class Usuario:
    email: str
    edad: int

    def __post_init__(self):
        # Validar formato de email
        if "@" not in self.email:
            raise ValueError(f"Email inválido: {self.email}")
        # Validar rango de edad
        if self.edad < 0 or self.edad > 150:
            raise ValueError(f"Edad inválida: {self.edad}")

Tuplas Nombradas

from typing import NamedTuple

class Punto(NamedTuple):
    """Punto 2D inmutable."""
    x: float
    y: float

    def distancia(self, otro: 'Punto') -> float:
        return ((self.x - otro.x) ** 2 + (self.y - otro.y) ** 2) ** 0.5

# Uso
p1 = Punto(0, 0)
p2 = Punto(3, 4)
print(p1.distancia(p2))  # 5.0

Decoradores

Decoradores de Función

import functools
import time

def temporizador(func: Callable) -> Callable:
    """Decorador para medir el tiempo de ejecución de una función."""
    @functools.wraps(func)
    def envoltura(*args, **kwargs):
        inicio = time.perf_counter()
        resultado = func(*args, **kwargs)
        elapsed = time.perf_counter() - inicio
        print(f"{func.__name__} tomó {elapsed:.4f}s")
        return resultado
    return envoltura

@temporizador
def funcion_lenta():
    time.sleep(1)

# funcion_lenta() imprime: funcion_lenta tomó 1.0012s

Decoradores Parametrizados

def repetir(veces: int):
    """Decorador para repetir una función múltiples veces."""
    def decorador(func: Callable) -> Callable:
        @functools.wraps(func)
        def envoltura(*args, **kwargs):
            resultados = []
            for _ in range(veces):
                resultados.append(func(*args, **kwargs))
            return resultados
        return envoltura
    return decorador

@repetir(veces=3)
def saludar(nombre: str) -> str:
    return f"¡Hola, {nombre}!"

# saludar("Alicia") devuelve ["¡Hola, Alicia!", "¡Hola, Alicia!", "¡Hola, Alicia!"]

Decoradores Basados en Clase

class ContadorLlamadas:
    """Decorador que cuenta cuántas veces se llama a una función."""
    def __init__(self, func: Callable):
        functools.update_wrapper(self, func)
        self.func = func
        self.contador = 0

    def __call__(self, *args, **kwargs):
        self.contador += 1
        print(f"{self.func.__name__} ha sido llamada {self.contador} veces")
        return self.func(*args, **kwargs)

@ContadorLlamadas
def procesar():
    pass

# Cada llamada a procesar() imprime el contador de llamadas

Patrones de Concurrencia

Hilos para Tareas con E/S

import concurrent.futures
import threading

def obtener_url(url: str) -> str:
    """Obtiene una URL (operación con E/S)."""
    import urllib.request
    with urllib.request.urlopen(url) as respuesta:
        return respuesta.read().decode()

def obtener_todas_urls(urls: list[str]) -> dict[str, str]:
    """Obtiene múltiples URLs concurrentemente usando hilos."""
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        futuro_a_url = {executor.submit(obtener_url, url): url for url in urls}
        resultados = {}
        for futuro in concurrent.futures.as_completed(futuro_a_url):
            url = futuro_a_url[futuro]
            try:
                resultados[url] = futuro.result()
            except Exception as e:
                resultados[url] = f"Error: {e}"
    return resultados

Multiprocesamiento para Tareas con Uso Intensivo de CPU

def procesar_datos(datos: list[int]) -> int:
    """Cómputo intensivo de CPU."""
    return sum(x ** 2 for x in datos)

def procesar_todos(conjuntos_datos: list[list[int]]) -> list[int]:
    """Procesa múltiples conjuntos de datos usando múltiples procesos."""
    with concurrent.futures.ProcessPoolExecutor() as executor:
        resultados = list(executor.map(procesar_datos, conjuntos_datos))
    return resultados

Async/Await para E/S Concurrente

import asyncio

async def obtener_async(url: str) -> str:
    """Obtiene una URL asincrónicamente."""
    import aiohttp
    async with aiohttp.ClientSession() as sesion:
        async with sesion.get(url) as respuesta:
            return await respuesta.text()

async def obtener_todas(urls: list[str]) -> dict[str, str]:
    """Obtiene múltiples URLs concurrentemente."""
    tareas = [obtener_async(url) for url in urls]
    resultados = await asyncio.gather(*tareas, return_exceptions=True)
    return dict(zip(urls, resultados))

Organización del Paquete

Estructura Estándar del Proyecto

mi_proyecto/
├── src/
│   └── mi_paquete/
│       ├── __init__.py
│       ├── principal.py
│       ├── api/
│       │   ├── __init__.py
│       │   └── rutas.py
│       ├── modelos/
│       │   ├── __init__.py
│       │   └── usuario.py
│       └── utilidades/
│           ├── __init__.py
│           └── ayudantes.py
├── pruebas/
│   ├── __init__.py
│   ├── conftest.py
│   ├── prueba_api.py
│   └── prueba_modelos.py
├── pyproject.toml
├── README.md
└── .gitignore

Convenciones de Importación

# Bueno: Orden de importación - stdlib, terceros, local
import os
import sys
from pathlib import Path

import requests
from fastapi import FastAPI

from mi_paquete.modelos import Usuario
from mi_paquete.utilidades import formatear_nombre

# Bueno: Usa isort para ordenamiento automático de importaciones
# pip install isort

init.py para Exportaciones del Paquete

# mi_paquete/__init__.py
"""mi_paquete - Un paquete Python de ejemplo."""

__version__ = "1.0.0"

# Exportar clases/funciones principales a nivel de paquete
from mi_paquete.modelos import Usuario, Publicacion
from mi_paquete.utilidades import formatear_nombre

__all__ = ["Usuario", "Publicacion", "formatear_nombre"]

Memoria y Rendimiento

Usando slots para Eficiencia de Memoria

# Malo: La clase regular usa __dict__ (más memoria)
class Punto:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

# Bueno: __slots__ reduce el uso de memoria
class Punto:
    __slots__ = ['x', 'y']

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

Generador para Datos Grandes

# Malo: Devuelve lista completa en memoria
def leer_lineas(ruta: str) -> list[str]:
    with open(ruta) as f:
        return [linea.strip() for linea in f]

# Bueno: Produce líneas una a la vez
def leer_lineas(ruta: str) -> Iterator[str]:
    with open(ruta) as f:
        for linea in f:
            yield linea.strip()

Evitar la Concatenación de Cadenas en Bucles

# Malo: O(n²) debido a la inmutabilidad de las cadenas
resultado = ""
for elemento in elementos:
    resultado += str(elemento)

# Bueno: O(n) usando join
resultado = "".join(str(elemento) for elemento in elementos)

# Bueno: Usando StringIO para construcción
from io import StringIO

buffer = StringIO()
for elemento in elementos:
    buffer.write(str(elemento))
resultado = buffer.getvalue()

Integración de Herramientas Python

Comandos Esenciales

# Formateo de código
black .
isort .

# Linting
ruff check .

# Verificación de tipo
mypy .

# Pruebas
pytest .

# Construir documentación
pdoc mi_paquete -o docs/

# Perfilado
python -m cProfile -o perfil.prof mi_script.py
python -m snakeviz perfil.prof

Configuración de pyproject.toml

# pyproject.toml
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "mi_paquete"
version = "1.0.0"
description = "Un proyecto Python de ejemplo"
requires-python = ">=3.9"
dependencies = [
    "requests>=2.28.0",
]
optional-dependencies = {
    "dev": [
        "black==23.1.0",
        "isort==5.12.0",
        "ruff==0.0.252",
        "mypy==1.0.0",
        "pytest==7.2.0",
    ]
}

[tool.black]
line-length = 88
target-version = ["py39"]

[tool.isort]
profile = "black"
line_length = 88

[tool.ruff]
line-length = 88
target-version = "py39"
select = ["E", "F", "I", "N", "W"]

[tool.mypy]
python_version = "3.9"
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_unimported = true
strict_equality = true

[tool.pytest.ini_options]
pythonpath = ["src"]
testpaths = ["pruebas"]

Convenciones de Estilo PEP 8

Nombrado

# Módulos: minúsculas_con_guiones_bajos
import mi_modulo

# Clases: MayúsculaInicial
class MiClase:
    pass

# Funciones y métodos: minúsculas_con_guiones_bajos
def mi_funcion():
    pass

# Constantes: MAYÚSCULAS_CON_GUIONES_BAJOS
VELOCIDAD_MAXIMA = 120

# Variables privadas: _prefijo_con_guiones_bajos
_dato_interno = None

# Variables de instancia y método: minúsculas_con_guiones_bajos
class Ejemplo:
    def __init__(self):
        self.mi_atributo = None

Longitud de Línea y Espaciado

# Bueno: Máximo 88 caracteres por línea (estándar Black)
def funcion_con_parametros_largos(
    parametro_uno: str,
    parametro_dos: int,
    parametro_tres: float,
    parametro_cuatro: bool,
) -> dict[str, Any]:
    pass

# Bueno: Dos líneas en blanco entre definiciones de nivel superior


class MiClase:
    pass


# Bueno: Una línea en blanco entre métodos dentro de la clase
class MiClase:
    def metodo_uno(self):
        pass

    def metodo_dos(self):
        pass

Docstrings

def funcion_ejemplo(parametro: str) -> bool:
    """
    Descripción breve de la función.

    Explicación más detallada si es necesario.

    Args:
        parametro: Descripción del parámetro.

    Returns:
        Descripción del valor de retorno.

    Raises:
        ValueError: Descripción de cuándo se lanza.
    """
    if not parametro:
        raise ValueError("Parámetro no puede estar vacío")
    return True

Notas Finales

  • Siempre escribe código legible y explícito. La legibilidad del código es primordial en Python.
  • Usa sugerencias de tipo para una mejor capacidad de mantenimiento y soporte de herramientas.
  • Maneja excepciones de forma específica y explícita en lugar de usar except: genéricos.
  • Aprovecha las características incorporadas de Python como comprensiones, generadores, clases de datos y administradores de contexto.
  • Elige la herramienta de concurrencia adecuada basada en si tu tarea está limitada por E/S o CPU.
  • Organiza tu código en paquetes con importaciones claras y explícitas.
  • Usa herramientas de automatización como Black, isort, ruff y mypy para mantener la calidad del código.
  • Recuerda: "Simple es mejor que complejo." Siempre que sea posible, prefiere la solución más simple que funcione.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados