Yeda AI Tips · #125

English

String += en un loop es O(n²), usa join

Este hábito de un solo carácter hace tu loop cuadrático. += sobre un string dentro de un loop parece inofensivo — un operador, una línea — pero en Python puede convertir una construcción de n elementos en trabajo O(n²). La solución también es una línea.

Por qué += en un loop se vuelve cuadrático

Los strings de Python son inmutables: ninguna operación modifica un string en su lugar. Así que s += part no agrega al final — asigna un string completamente nuevo y copia todo s más part, en cada iteración.

Hazlo n veces y el copiado crece: 1 carácter, luego 2, luego 3 … aproximadamente n²/2 copias de caracteres en total. El propio FAQ de Python lo dice claro: concatenar muchos strings así tiene un costo total de ejecución "cuadrático en la longitud total del string".

Con 100 partes no lo notarás. Con 100,000 líneas de un log o un reporte generado, el loop que debería tomar milisegundos toma minutos — y empeora cuanto más grande es la entrada, justo cuando menos lo puedes permitir.

La solución: junta las partes, une una vez

El idiom recomendado, directo del FAQ de Python: acumula las piezas en una lista y llama str.join() una sola vez al final.

# O(n²): copies the whole string every pass
result = ""
for row in rows:
    result += render(row)

# O(n): one allocation, one copy at the end
parts = []
for row in rows:
    parts.append(render(row))
result = "".join(parts)

list.append es O(1) amortizado, y "".join(parts) mide la longitud total, asigna memoria una vez y copia cada parte exactamente una vez. Misma salida, tiempo lineal. Si el cuerpo del loop es simple, compáctalo más: result = "".join(render(row) for row in rows).

Construcción incremental: io.StringIO

Cuando construyes texto de forma incremental entre funciones — un generador de código, un acumulador de tokens de un LLM, cualquier cosa con forma de writer — una lista de partes puede resultar incómoda. io.StringIO te da un buffer de texto en memoria con API de archivo, y el FAQ de Python lo lista como el otro idiom razonablemente eficiente:

import io

buf = io.StringIO()
for chunk in stream:
    buf.write(chunk)
result = buf.getvalue()

Bonus: cualquier cosa que espere un objeto de archivo (csv.writer, print(file=...), writers de plantillas) puede escribir directo al buffer.

Reglas prácticas

SituaciónUsa
Unas pocas piezas conocidas+ simple o un f-string
Loop que construye un string a partir de n partesLista + "".join(parts)
Construcción incremental estilo writer, APIs de archivoio.StringIO
Acumular bytes en un loopbytearray + += (es mutable, así que esto SÍ es eficiente)

Notas para usuarios avanzados

Recursos

¿Construyendo una funcionalidad con AI? Yeda AI diseña, audita y entrega sistemas LLM de producción.

Habla con nosotros · Lee el blog · Read in English