Entendiendo las Puntuaciones de Complejidad¶
¿Qué Significan los Números?¶
Las puntuaciones de complejidad cognitiva representan el esfuerzo mental requerido para entender un fragmento de código. Las puntuaciones más altas indican código más difícil de comprender y mantener.
Umbrales Recomendados¶
| Rango de Puntuación | Interpretación | Recomendación |
|---|---|---|
| 0-5 | Simple | Fácil de entender, no se requiere acción |
| 6-10 | Moderado | Generalmente aceptable, pero con vigilancia ante mayor crecimiento |
| 11-15 | Complejo | Considerar refactorización si se está añadiendo funcionalidad |
| 16-25 | Alto | Refactorización recomendada |
| 26+ | Muy Alto | Refactorización fuertemente recomendada |
Umbral Predeterminado
complexipy usa un umbral predeterminado de 15. Las funciones que superen esta puntuación activarán advertencias.
Cómo se Calcula la Complejidad¶
La complejidad cognitiva se calcula analizando el Árbol de Sintaxis Abstracta (AST) de tu código Python y aplicando estas reglas:
1. Incrementos de Complejidad Base¶
Cada estructura de flujo de control añade a la complejidad:
| Estructura | Puntuación | Ejemplo |
|---|---|---|
Sentencia if |
+1 | if condition: |
Cláusula elif |
+1 | elif other_condition: |
Cláusula else |
+0 | else: (solo anidamiento) |
Bucle for |
+1 | for item in items: |
Bucle while |
+1 | while condition: |
Manejador except |
+1 | except ValueError: |
Cláusula finally |
+0 | finally: (solo anidamiento) |
| Operador ternario | +1 | x if condition else y |
2. Multiplicador de Anidamiento¶
Esta es la innovación clave: las estructuras anidadas añaden complejidad extra basada en su profundidad.
def example():
if a: # +1 (base)
if b: # +2 (1 base + 1 anidamiento)
if c: # +3 (1 base + 2 anidamiento)
pass
# Complejidad total: 6
La fórmula es: complexity = 1 + nesting_level
3. Operadores Booleanos¶
Las condiciones lógicas complejas aumentan la carga cognitiva:
# Puntuación: 3
def check(a, b, c):
if a and b or c: # +1 (if) +2 (operadores booleanos)
return True
- Cada operador
anduor: +1 - Cambios de tipo de operador (mezclar
and/or): +1 adicional
4. Casos Especiales¶
Break y Continue
# Puntuación: 3
for item in items: # +1
if condition: # +2 (1 + anidamiento)
break # +0 (sin puntuación adicional)
Sentencias Match (Python 3.10+)
# Puntuación: 2
match value: # +0 (el match en sí no cuenta)
case 1:
if x: # +1
pass
case 2:
if y: # +1
pass
Recursión Las llamadas recursivas se analizan pero no añaden complejidad extra más allá de sus estructuras de control.
Ejemplos Detallados¶
Ejemplo 1: Lógica Secuencial Simple¶
def process_user(user):
if not user: # +1
return None
if user.is_active: # +1
send_welcome_email()
if user.needs_verification: # +1
send_verification()
return user
# Complejidad total: 3
Análisis: Tres sentencias if secuenciales, sin anidamiento. Fácil de seguir.
Ejemplo 2: Lógica Anidada¶
def validate_order(order):
if order: # +1
if order.items: # +2 (1 + anidamiento)
for item in order.items: # +3 (1 + anidamiento)
if item.quantity > 0: # +4 (1 + anidamiento)
process(item)
return False
# Complejidad total: 10
Análisis: El anidamiento profundo crea un crecimiento exponencial en la carga cognitiva.
Ejemplo 3: Condiciones Complejas¶
def check_eligibility(user, product):
if user.age >= 18 and user.verified and product.available: # +4
# ^^^ if: +1, operadores and: +2, total: +4
return True
return False
# Complejidad total: 4
Análisis: Múltiples operadores booleanos aumentan la carga mental para comprender la condición.
Ejemplo 4: Manejo de Excepciones¶
def load_config(path):
try: # try en sí mismo: +0
with open(path) as f: # +1 (gestor de contexto tratado como if)
data = json.load(f)
if validate(data): # +2 (1 + anidamiento)
return data
except FileNotFoundError: # +1
create_default_config()
except json.JSONDecodeError: # +1
log_error()
finally: # +0 (finally en sí mismo)
if log_enabled: # +1
log("Config load attempted")
# Complejidad total: 6
Directrices Prácticas¶
¿Qué Debería Refactorizar?¶
Enfócate en las funciones con puntuaciones por encima de tu umbral (predeterminado: 15). Estrategias comunes de refactorización:
- Extraer Métodos - Mover la lógica anidada a funciones separadas
- Retornos Anticipados - Usar cláusulas de
returnpara reducir el anidamiento - Simplificar Condiciones - Dividir expresiones booleanas complejas en variables con nombre
- Patrón Estrategia - Reemplazar if/else anidados con polimorfismo
Ejemplo de Refactorización¶
Antes (Complejidad: 12)
def process_order(order):
if order: # +1
if order.is_valid(): # +2
if order.payment: # +3
if order.payment.process(): # +4
ship_order(order)
else:
refund(order) # +0 (cláusula else)
else:
notify_payment_required() # Aún anidado
else:
log_invalid_order()
return False
Después (Complejidad: 4)
def process_order(order):
if not order: # +1
return False
if not order.is_valid(): # +1
log_invalid_order()
return False
if not order.payment: # +1
notify_payment_required()
return False
if order.payment.process(): # +1
ship_order(order)
return True
refund(order)
return False
Mejoras clave: - Anidamiento reducido de 4 niveles a 1 - Complejidad reducida de 12 a 4 - La lógica ahora es lineal y más fácil de seguir
Consejos para Interpretar Puntuaciones¶
- El Contexto Importa - Una puntuación de 20 puede ser aceptable para un algoritmo complejo pero problemática para lógica de negocio
- Tendencia en el Tiempo - Observa la complejidad creciente en funciones que modificas frecuentemente
- Puntuaciones Relativas - Compara funciones dentro de la misma base de código para identificar valores atípicos
- Acuerdo del Equipo - Establece umbrales que funcionen para tu equipo y proyecto