2012-09-14 6 views
5

La documentazione di Python 3.2 s' weakref di WeakKeyDictionary e WeakValueDictionary hanno una nota sulla iterazione di questi contenitori modulo:in modo sicuro l'iterazione di WeakKeyDictionary e WeakValueDictionary

Nota: Attenzione: A causa di un WeakKeyDictionary è costruita su in cima a un dizionario Python, non deve cambiare le dimensioni quando viene iterato su di esso. Questo può essere difficile da garantire per un WeakKeyDictionary perché le azioni eseguite dal programma durante l'iterazione possono far sì che gli elementi nel dizionario svaniscano "per magia" (come effetto collaterale della garbage collection).

Che sembra piuttosto terribile come una specifica del comportamento di questi contenitori. Soprattutto quando si esegue un codice che usa il garbage collector di CPython (quando si usano strutture dati che contengono un ciclo) o si utilizza un'altra implementazione Python (ad esempio Jython), allora sembra che non ci sia un modo sicuro per iterare su queste raccolte.

Come posso eseguire un'iterazione sicura su queste raccolte quando il garbage collector può cancellare i riferimenti in qualsiasi punto del mio programma? Avere una soluzione per CPython è la mia priorità, ma sono interessato anche al problema di altre implementazioni.

È forse un modo sicuro per scorrere su un WeakKeyDictionary?

import weakref 

d = weakref.WeakKeyDictionary() 

... 

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

risposta

6

Per sicurezza è necessario mantenere un riferimento da qualche parte. Utilizzando il linguaggio:

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

non è completamente sicuro, perché, anche se funzionerà il più delle volte, durante l'ultima iterazione del ciclo l'elenco può essere garbage collection.

Il modo corretto sarebbe:

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

Se si utilizza un WeakKeyDictionary si può semplicemente memorizzare le chiavi, e memorizzare i valori se si utilizza WeakValueDictionary.

Nota a margine: in python2 .items() già restituisce un elenco.

In definitiva dipende da cosa intendi per "sicuro". Se semplicemente dire che l'iterazione procederà correttamente (iterazione una volta su tutti gli elementi), quindi:

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

è sicuro, perché l'iterazione il dizionario sia eseguito effettivamente da list(d.items()), allora si effettua l'iterazione solo il elenco.

Se, invece, si intende che durante l'iterazione gli elementi non devono "scomparire" dal dizionario come effetto collaterale dello for -loop, è necessario mantenere un riferimento forte fino alla fine del ciclo e ciò richiede per memorizzare l'elenco in una variabile prima di iniziare il ciclo.

+2

Perché il tuo primo esempio non sarebbe sicuro? L'elenco conterrà forti riferimenti a ogni chiave e valore e durante l'ultima iterazione, 'k' e' v' mantengono forti riferimenti agli oggetti a cui sono interessato. Quindi la lista può essere raccolta inutilmente prima che l'ultima iterazione termini. È giusto? – Feuermurmel

+0

È come dire che 'per k, v in d.items()' è sicuro, perché 'k' e' v' contengono riferimenti forti agli oggetti.L'iterazione non è sicura se, all'interno del ciclo for, esiste la possibilità di eliminare 'k' e' v'. Per compiti abbastanza semplici, iterare su 'WeakKeyDictionary' dovrebbe essere sicuro. – Bakuriu

+3

Probabilmente sto fraintendendo qualcosa, ma come possono essere cancellati gli oggetti 'k' e' v'? Finché queste variabili sono in ambito e non sono nascoste, gli oggetti di riferimento sono sicuri. O stai parlando di rimuovere tutti i riferimenti forti a quegli oggetti durante l'ultima iterazione? Ciò cambierebbe il dizionario, ma non sarebbe pericoloso perché il dizionario non è accessibile dopo l'iterazione è iniziata. Puoi dare un esempio di cosa potrebbe andare storto durante l'ultima iterazione? – Feuermurmel