¿Por qué no es seguro modificar la secuencia que está siendo iterada?

No es seguro modificar la secuencia que se está iterando en el bucle (esto solo puede suceder con los tipos de secuencia mutables, como las listas). Si necesita modificar la lista sobre la que está iterando (por ejemplo, para duplicar elementos seleccionados), debe iterar sobre una copia. La notación de corte lo hace particularmente conveniente:

>>> for x in a[:]: # make a slice copy of the entire list ... if len(x) > 6: a.insert(0, x) ... >>> a ['defenestrate', 'cat', 'window', 'defenestrate'] 

¿Por qué no es seguro hacer solo for x in a ?

Sin llegar a ser demasiado técnico:

Si estás iterando a través de una secuencia mutable en Python y la secuencia se modifica mientras se está iterando, no siempre queda claro qué sucederá. Si inserta un elemento en la secuencia mientras lo recorre, ¿qué se consideraría razonablemente el “siguiente” elemento de la secuencia? ¿Qué pasa si borras el siguiente objeto?

Por esta razón, la iteración a través de una secuencia mutable mientras la cambias conduce a un comportamiento no especificado. Cualquier cosa puede suceder, dependiendo exactamente de cómo se implementa la lista. 🙂

Este es un problema común en muchos idiomas. Si tiene una estructura de datos lineal y está iterando sobre ella, algo debe hacer un seguimiento de dónde se encuentra en la estructura. Puede ser un índice actual o un puntero, pero es una especie de dedo que apunta al “lugar actual”.

Si modifica la lista mientras ocurre la iteración, es probable que el cursor sea incorrecto.

Un problema común es que se elimina el elemento debajo del cursor, todo se desliza hacia abajo, la siguiente iteración del bucle incrementa el cursor y se saltó un elemento inadvertidamente.

Algunas implementaciones de la estructura de datos ofrecen la posibilidad de eliminar elementos mientras se iteran, pero la mayoría no lo hace.

Cuando modifica una colección que está iterando sobre el iterador puede comportarse de manera inesperada, por ejemplo, perder elementos o devolver el mismo elemento dos veces.

Este código se repite indefinidamente cuando lo ejecuto:

 >>> a = [ 'foo', 'bar', 'baz' ] >>> for x in a: ... if x == 'bar': a.insert(0, 'oops') 

Esto se debe a que el iterador utiliza el índice para realizar un seguimiento de dónde se encuentra en la lista. Al agregar un elemento al comienzo de la lista, se vuelve a devolver la ‘barra’ del elemento en lugar de que el iterador avance al siguiente elemento.