2011-11-17 9 views
15

Sono venuto a questo comportamento che mi ha sorpreso:Creazione di dizionari con tasti e oggetti mutabili. Una sorpresa

Python 2.6 e 3,2

>>> xs = dict.fromkeys(range(2), []) 
>>> xs 
{0: [], 1: []} 
>>> xs[0].append(1) 
>>> xs 
{0: [1], 1: [1]} 

Tuttavia, comprensioni dict in 3.2 mostrano un atteggiamento più gentile:

>>> xs = {i:[] for i in range(2)} 
>>> xs 
{0: [], 1: []} 
>>> xs[0].append(1) 
>>> xs 
{0: [1], 1: []} 
>>> 

Come viene? Perché fromkeys si comporta in questo modo?

+1

la differenza è la stessa come in '[[] ] * 2' e '[[] per _ nell'intervallo (2)]'. – jfs

+0

@ J.F.Sebastian Sono abituato al significato di [[]] * 2 e altri trucchi simili. Ma i tasti mi hanno colto di sorpresa. Forse è solo una questione di familiarità ... praticamente non uso mai il metodo fromkeys ... – joaquin

risposta

17

tuo Python 2.6 esempio è equivalente a quanto segue, che può aiutare a chiarire:

>>> a = [] 
>>> xs = dict.fromkeys(range(2), a) 

Se il valore predefinito in fromkeys() è un oggetto mutabile, ogni voce nel dizionario risultante avrà un riferimento al stesso oggetto, come hai visto.

>>> xs[0] is a and xs[1] is a 
True 

Anche se Python 2.6 e meno recenti non supportano comprensioni del dizionario, è possibile ottenere lo stesso comportamento utilizzando dict() con un generatore di espressione:

xs = dict((i, []) for i in range(2)) 
+2

Ogni voce nel dizionario 'xs' risultante avrà un riferimento allo stesso oggetto' a' come suo valore, anche se 'a' capita di essere mutabile. Ma ovviamente il problema nell'OP si verifica solo quando 'a' è mutabile e lo si modifica. –

5

Nella prima versione, si utilizza l'oggetto lista vuota stessa come valore per entrambi i tasti, quindi se si cambia uno, cambiare l'altro, anche.

Guardate questa:

>>> empty = [] 
>>> d = dict.fromkeys(range(2), empty) 
>>> d 
{0: [], 1: []} 
>>> empty.append(1) # same as d[0].append(1) because d[0] references empty! 
>>> d 
{0: [1], 1: [1]} 

Nella seconda versione, un nuovo oggetto elenco vuoto viene creato in ogni iterazione della comprensione dict, quindi entrambi sono indipendenti l'uno dall'altro.

Come "perché" fromkeys() funziona così - beh, sarebbe sorprendente se non funzionasse così. fromkeys(iterable, value) crea un nuovo dict con chiavi da iterable che hanno tutte il valore value. Se quel valore è un oggetto mutabile e tu cambi questo oggetto, cos'altro potresti ragionevolmente aspettarti di accadere?

+3

Tim, capisco perché succede. La mia domanda è più simile a "perché è progettata per comportarsi in questo modo?". Mi dispiace non essere chiaro nella domanda. – joaquin

Problemi correlati