2012-09-14 15 views
5

La documentación de Python 3.2 's weakref de WeakKeyDictionary y WeakValueDictionary tienen una nota en la iteración en estos contenedores módulo:iteración de forma segura sobre WeakKeyDictionary y WeakValueDictionary

Nota: Precaución: Debido a que un WeakKeyDictionary se basa en parte superior de un diccionario de Python, no debe cambiar el tamaño al iterar sobre él. Esto puede ser difícil de asegurar para un WeakKeyDictionary porque las acciones realizadas por el programa durante la iteración pueden hacer que los elementos en el diccionario desaparezcan "por arte de magia" (como un efecto secundario de la recolección de basura).

Eso parece bastante grave como una especificación del comportamiento de estos contenedores. Especialmente cuando se ejecuta código que utiliza el recolector de basura de CPython (cuando se usan estructuras de datos que contienen ciclos) o usando otra implementación de Python (por ejemplo, Jython), parece que no hay forma segura de iterar sobre estas colecciones.

¿Cómo puedo iterar con seguridad sobre estas colecciones cuando el recolector de basura puede borrar referencias en cualquier punto de mi programa? Tener una solución para CPython es mi prioridad, pero también me interesa el problema en otras implementaciones.

¿Es esta quizás una forma segura de iterar sobre un WeakKeyDictionary?

import weakref 

d = weakref.WeakKeyDictionary() 

... 

for k, v in list(d.items()): 
    ... 

Respuesta

6

Para estar seguro, debe mantener una referencia en algún lugar. Usando el lenguaje:

for k,v in list(d.items()): 

no es completamente seguro, ya que, a pesar de que va a trabajar la mayor parte del tiempo, durante la última iteración del bucle de la lista puede ser recolección de basura.

La forma correcta sería:

items = list(d.items()) 
for k,v in items: 
    #do stuff that doesn't have a chance of destroying "items" 
del items 

Si utiliza un WeakKeyDictionary simplemente podría almacenar las claves, y almacenar los valores si utiliza WeakValueDictionary.

En una nota al margen: en python2 .items() ya devuelve una lista.

En última instancia, depende de lo que quiere decir con "seguro". Si simplemente quiere decir que la iteración no puede hacerse correctamente (la iteración de una vez en todos los elementos), entonces:

for k,v in list(d.items()): 

es seguro, ya que la iteración sobre el diccionario es efectuado por list(d.items()), a continuación, sólo se le interactuando sobre la lista.

Si, en cambio, significa que durante la iteración los elementos no deberían "desaparecer" del diccionario como efecto secundario del for -loop, entonces debe mantener una referencia fuerte hasta el final del ciclo, y esto requiere usted para almacenar la lista en una variable antes de comenzar el ciclo.

+2

¿Por qué su primer ejemplo sería inseguro? La lista contendrá fuertes referencias a cada clave y valor, y durante la última iteración, 'k' y' v' contienen fuertes referencias a los objetos que me interesan. Por lo tanto, la lista puede ser basura incluso antes de que termine la última iteración. ¿Está bien? – Feuermurmel

+0

Eso es como decir que 'para k, v en d.items()' es seguro, porque 'k' y' v' contienen fuertes referencias a los objetos.La iteración no es segura si, dentro del for-loop, existe la posibilidad de eliminar 'k' y' v'. Para las tareas simples, iterar sobre el 'WeakKeyDictionary' debe ser seguro. – Bakuriu

+3

Probablemente esté malinterpretando algo, pero ¿cómo se puede eliminar la referencia de los objetos 'k' y' v'? Siempre que estas variables estén dentro del alcance y no estén sobrepasadas, los objetos a los que se hace referencia son seguros. ¿O estás hablando de eliminar todas las referencias fuertes a esos objetos durante la última iteración? Eso cambiaría el diccionario, pero no será inseguro porque no se accede al diccionario después de que la iteración haya comenzado. ¿Puedes dar un ejemplo de lo que puede salir mal durante la última iteración? – Feuermurmel

Cuestiones relacionadas