10 - Micro-optimizaciones en Python

Una vez elegido el algoritmo correcto, quedan mejoras pequeñas para aprovechar CPU y memoria. No cambian el Big-O, pero reducen constantes y evitan trampas frecuentes.

10.1 Costo de comparar objetos

Comparar enteros es directo; comparar objetos puede implicar evaluar varios atributos.

class Punto:
  def __init__(self, x, y, activo):
    self.x = x
    self.y = y
    self.activo = activo

def iguales(a, b):
  return a.x == b.x and a.y == b.y and a.activo == b.activo

Si solo importa un campo para ordenar o filtrar, compara ese campo y evita reconstruir objetos completos.

10.2 Costo de crear objetos en loops

Crear objetos en cada iteración agrega overhead y presiona al recolector de basura.

# Evitar: crear lista nueva en cada paso
for i in range(n):
  tmp = [i]
  usar(tmp)

# Mejor: reutilizar estructura cuando aplica
tmp = [0]
for i in range(n):
  tmp[0] = i
  usar(tmp)

Cuando es posible, reserva una vez fuera del loop o usa estructuras prealocadas.

10.3 Reducir llamadas dentro del loop

Llamar funciones pequeñas agrega overhead de llamada y lookup en Python.

def cuadrado(x):
  return x * x

def suma_cuadrados(valores):
  total = 0
  for x in valores:
    total += x * x  # en lugar de cuadrado(x) si es un hotspot
  return total

En secciones críticas, mover el cálculo directo o usar funciones locales puede reducir el overhead.

10.4 Movimiento de código común fuera del loop

Cualquier cálculo que no dependa del índice debe moverse fuera del ciclo.

def escala_vector(valores, factor):
  k = 1.0 / factor  # calculado una vez
  for i in range(len(valores)):
    valores[i] *= k
  return k

Esto reduce operaciones repetidas y mejora la localidad de datos.

10.5 Funciones simples y expresiones directas

En Python no hay macros, pero conviene evitar evaluaciones repetidas.

def menor_de_tres(a, b, c):
  return min(a, b, c)

Prefiere funciones built-in (como min, max, sum) que ya están optimizadas en C.

10.6 Uso inteligente de referencias

  • Variables locales: guardar referencias locales evita lookups repetidos en objetos o módulos.
  • Evitar aliasing innecesario: mantener variables claras reduce errores y mejoras en legibilidad.
  • Iterar directo: preferir iteradores sobre acceso por índice cuando no hace falta el índice.
def suma_iterando(valores):
  total = 0
  for valor in valores:
    total += valor
  return total