Watch at EaseHistorias. Respuestas. Perspectiva.

Tecnología · GUÍA PRÁCTICA

¿Python dice “dictionary changed size during iteration”? Demuestra quién cambió el diccionario antes de copiar o bloquear

Separa cambios del mismo bucle, escritores concurrentes, callbacks reentrantes y registros del runtime antes de elegir una reparación verificable.

Flujo original desde el traceback de iteración hasta demostrar el mutador y aceptar la reparación
Ilustración: demuestra el mutador antes de elegir copia, bloqueo o arreglo de dependencia. Watch at Ease

RuntimeError: dictionary changed size during iteration es una señal de límite: el conjunto de claves cambió mientras alguien recorría el mapping. No identifica si el autor fue el mismo bucle, otro hilo, un callback o un registro de Python o de una dependencia.

Conserva el traceback, demuestra propietario y mutador, elige instantánea, cambio en dos fases, sincronización compartida o corrección upstream, y valida invariantes bajo el entrelazado que fallaba.

1. Conserva el traceback e identifica el mapping

Guarda traceback completo, versiones de Python y paquetes, nombre de hilo o tarea e identidad estable del mapping. Determina si falla tu bucle, deepcopy, weakref, sys.modules, un ejecutor o una dependencia.

Anota si recorres d, keys, values o items y si cambia el conjunto de claves o solo un valor existente; un callback durante esa actualización todavía puede añadir o borrar claves.

2. Reproduce una clase de mutador cada vez

Desactiva callbacks y escritores de fondo. Si aún falla, busca delete, pop, clear, setdefault de clave ausente o inserción en el bucle y sus llamadas. Si solo falla con carga, instrumenta cada ruta de escritura.

Un frame en deepcopy o sorted no prueba que esas funciones mutaran. Callbacks de referencias débiles, limpieza, comparadores, logging, señales o destructores pueden reentrar.

3. Elige instantánea o cambio en dos fases de forma explícita

Si una vista puntual es correcta, usa list(d), list(d.items()), tuple(d) o d.copy() según la semántica. La copia superficial estabiliza claves, no los valores mutables ni lecturas compuestas posteriores.

Si el bucle decide qué cambiar, recopila decisiones sobre una vista estable y aplícalas después. Para consumo destructivo intencional, un bucle popitem documentado expresa mejor el contrato.

4. Haz real la propiedad en concurrencia y reentrada

Con varios hilos, protege lecturas consistentes y todas las escrituras estructurales con el mismo Lock/RLock, o asigna la propiedad a un hilo y usa una cola. Bloquear solo al lector no coordina escritores sin bloqueo.

No llames código desconocido manteniendo un bloqueo arbitrario. Toma una instantánea acotada dentro del límite, libera y llama al callback, o difiere efectos a una segunda fase.

5. Trata registros del runtime como incidentes de dependencia

Si el traceback termina en sys.modules, weakref, ProcessPoolExecutor o un gestor de plugins, reproduce en una versión soportada, revisa el proyecto y conserva caso mínimo y versión corregida.

La aceptación debe forzar el entrelazado antiguo, comprobar claves sin pérdidas ni duplicados, supuestos de orden, callbacks una sola vez y obsolescencia intencional. Solo evitar la excepción no basta.

Matriz de mutación en el mismo bucle, concurrente, reentrante y propiedad del runtime
Ilustración: el mismo RuntimeError puede requerir cuatro reparaciones distintas. Watch at Ease