2012-01-21 19 views
121

In python 2.7, abbiamo il dictionary view methods disponibile.Cosa sono gli oggetti vista dizionario?

Ora, so che i pro ei contro della seguente:

  • dict.items() (e values, keys): restituisce una lista, in modo da poter effettivamente memorizzare il risultato
  • dict.iteritems() (e simili) : restituisce un generatore, quindi puoi iterabile su ogni valore generato uno per uno.

Per cosa sono disponibili dict.viewitems() (e simili)? Quali sono i loro benefici? Come funziona? Qual è una visione, dopo tutto?

Ho letto che la vista riflette sempre i cambiamenti dal dizionario. Ma come si comporta dal punto di vista del perf e della memoria? Quali sono i pro e i contro?

risposta

118

Le viste del dizionario sono essenzialmente ciò che dice il loro nome: le viste sono semplicemente come una finestra sulle chiavi e valori (o elementi) di un dizionario. Ecco un estratto dal official documentation per Python 3:

>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500} 
>>> keys = dishes.keys() 
>>> values = dishes.values() 

>>> # view objects are dynamic and reflect dict changes 
>>> del dishes['eggs'] 
>>> keys # No eggs anymore! 
dict_keys(['sausage', 'bacon', 'spam']) 

>>> values # No eggs value (2) anymore! 
dict_values([1, 1, 500]) 

(L'equivalente Python 2 utilizza dishes.viewkeys() e dishes.viewvalues().)

Questo esempio illustra il carattere dinamico di visite: visualizzare le chiavi non è una copia delle chiavi in ​​un dato momento, ma piuttosto una semplice finestra che mostra le chiavi; se sono cambiati, anche quello che vedi attraverso la finestra cambia pure. Questa funzione può essere utile in alcune circostanze (ad esempio, si può lavorare con una vista sui tasti in più parti di un programma invece di ricalcolare l'elenco corrente di chiavi ogni volta che sono necessarie).

Un vantaggio è che guardando a, diciamo, i tasti utilizza solo una piccola e fissa quantità di memoria e richiede una piccola e fissato tempo di elaborazione, in quanto non v'è alcuna creazione di un elenco di keys (Python 2, d'altra parte, crea spesso inutilmente una nuova lista, come citato da Rajendran T, che prende memoria e tempo in un ammontare proporzionale alla lunghezza della lista). Per continuare l'analogia con le finestre, se vuoi vedere un paesaggio dietro un muro, fai semplicemente un'apertura (costruisci una finestra); copiare le chiavi in ​​una lista equivarrebbe a dipingere invece una copia del paesaggio sul muro: la copia richiede tempo, spazio e non si aggiorna automaticamente.

Per riassumere, le viste sono semplicemente ... viste (finestre) sul dizionario, che mostrano il contenuto del dizionario anche dopo la modifica. Offrono funzionalità che differiscono da quelle degli elenchi: un elenco di chiavi contiene una copia delle chiavi del dizionario in un determinato momento, mentre una vista è dinamica ed è molto più veloce da ottenere, poiché non deve copiare alcun dati (chiavi o valori) per essere creati.

+5

+1. Ok, in cosa differisce dall'avere accesso diretto alla lista interna delle chiavi? È più veloce, più lento? Più memoria efficiente? Limitato? Se puoi leggerlo e modificarlo, sembra esattamente come avere un riferimento a questo elenco. –

+2

Grazie. Il fatto è che le viste * sono * il tuo accesso a "la lista interna delle chiavi" (nota che questa "lista di chiavi" non è una lista Python, però, ma è proprio una vista). Le viste hanno una memoria più efficiente rispetto agli elenchi di chiavi (o valori o elementi) di Python 2, poiché non copiano nulla; sono in effetti come "un riferimento alla lista delle chiavi" (si noti anche che "un riferimento ad una lista" in realtà è semplicemente chiamato una lista, in Python, dato che le liste sono oggetti mutabili). Si noti inoltre che non è possibile modificare direttamente le viste: al contrario, si modifica ancora il dizionario e le viste riflettono immediatamente le modifiche. – EOL

+2

Ok, non sono ancora chiaro sull'implementazione, ma è la risposta migliore finora. –

13

I metodi vista restituire un elenco (non una copia della lista, rispetto al .keys(), .items() e .values()), quindi è più leggera, ma riflette il contenuto attuale del dizionario.

Da Python 3.0 - dict methods return views - why?

Il motivo principale è che per molti casi d'uso che ritornano una lista completamente distaccata è inutile e dispendioso. Richiederebbe copiare l'intero contenuto (che può essere o molti non molto).

Se si desidera semplicemente eseguire un'iterazione sui tasti, non è necessario creare un nuovo elenco . E se effettivamente ne hai bisogno come lista separata (come copia ), puoi facilmente creare quell'elenco dalla vista.

+2

+1 ma ancora non è chiaro che vista è e come funziona. –

+4

I metodi di visualizzazione restituiscono oggetti di visualizzazione, che non sono conformi all'interfaccia dell'elenco. –

16

Proprio dalla lettura dei documenti ho questa impressione:

  1. Visualizzazioni sono "pseudo-set-like", nel senso che non supportano l'indicizzazione, quindi cosa si può fare con loro è di prova per l'appartenenza e iterate su di loro (poiché le chiavi sono lavabili e uniche, le visualizzazioni di chiavi e oggetti sono più "set-like" in quanto non contengono duplicati).
  2. È possibile memorizzarli e utilizzarli più volte, come le versioni di elenco.
  3. Poiché riflettono il dizionario sottostante, qualsiasi modifica nel dizionario cambierà la visualizzazione e cambierà quasi certamente l'ordine di iterazione . Quindi, a differenza delle versioni di lista, non sono "stabili".
  4. Poiché riflettono il dizionario sottostante, sono quasi certamente piccoli oggetti proxy; copiare le chiavi/valori/elementi richiederebbe che guardassero il dizionario originale in qualche modo e lo copiassero più volte in caso di cambiamenti, il che sarebbe un'implementazione assurda. Quindi mi aspetterei pochissimo sovraccarico di memoria, ma l'accesso potrebbe essere un po 'più lento rispetto al dizionario.

Quindi immagino che la chiave d'uso sia se si tiene un dizionario in giro e ripetutamente ripetendo le sue chiavi/articoli/valori con modifiche intermedie. Potresti semplicemente utilizzare invece una vista, trasformando for k, v in mydict.iteritems(): in for k, v in myview:. Ma se stai solo ripetendo il dizionario una volta, penso che le versioni iter siano ancora preferibili.

+2

+1 per analizzare i pro ei contro dalle poche informazioni che abbiamo ottenuto. –

+0

Se creo un iteratore su una vista, esso viene comunque invalidato ogni volta che il dizionario cambia. Questo è lo stesso problema con un iteratore sul dizionario stesso (per esempio 'iteritems()'). Quindi qual è il punto di queste opinioni? Quando sono felice di averli? – Alfe

+0

@Alfe Hai ragione, questo è un problema con l'iterazione del dizionario e le viste non aiutano affatto. Supponi di dover passare i valori di un dizionario a una funzione. Si potrebbe usare '.values ​​()', ma ciò comporta la creazione di una copia intera come lista, che potrebbe essere costosa. C'è '.itervalues ​​()' ma non puoi consumarli più di una volta, quindi non funzionerà con tutte le funzioni. Le viste non richiedono una copia costosa, ma sono comunque più utili come valore indipendente rispetto a un iteratore. Ma non hanno ancora lo scopo di aiutare con l'iterazione e la modifica allo stesso tempo (ce ne vuole davvero una copia). – Ben

16

Come hai menzionato, dict.items() restituisce una copia dell'elenco di coppie (chiave, valore) del dizionario che è uno spreco e dict.iteritems() restituisce un iteratore sulle coppie del dizionario (chiave, valore).

Ora l'esempio seguente per vedere la differenza tra un interator di dict e una vista del dict

>>> d = {"x":5, "y":3} 
>>> iter = d.iteritems() 
>>> del d["x"] 
>>> for i in iter: print i 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
RuntimeError: dictionary changed size during iteration 

Mentre una vista semplicemente vi mostra cosa c'è nel dict. Non importa se è cambiato:

>>> d = {"x":5, "y":3} 
>>> v = d.viewitems() 
>>> v 
dict_items([('y', 3), ('x', 5)]) 
>>> del d["x"] 
>>> v 
dict_items([('y', 3)]) 

Una vista è semplicemente una cosa del dizionario assomiglia ora. Dopo aver eliminato una voce .items() sarebbe stato scaduto e .iteritems() avrebbe generato un errore.

+0

Grande esempio, grazie. Tuttavia, dovrebbe essere v = d.items() not v - d.viewitems() – rix

+0

La domanda riguarda Python 2.7, quindi 'viewitems()' è effettivamente corretto ('items()' dà una vista in Python ** 3 **). – EOL

+0

Tuttavia, una vista ** non può ** essere utilizzata per iterare su un dizionario durante la modifica. –

0

Views consente di accedere alla struttura dati sottostante, senza copiarlo. Oltre ad essere dinamico rispetto alla creazione di un elenco, uno dei loro utilizzo più utile è il test in. Supponi di voler controllare se un valore è in dict o meno (o chiave o valore).

L'opzione 1 consiste nel creare un elenco di chiavi utilizzando dict.keys(), ma funziona, ma ovviamente consuma più memoria. Se il dict è molto grande? Sarebbe uno spreco.

Con views è possibile iterare la struttura dati effettiva, senza lista intermedia.

Facciamo un esempio. Ho un dict con 1000 chiavi di stringhe casuali e cifre e k è la chiave che voglio cercare

large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. } 

>>> len(large_d) 
1000 

# this is one option; It creates the keys() list every time, it's here just for the example 
timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000) 
13.748743600954867 


# now let's create the list first; only then check for containment 
>>> list_keys = large_d.keys() 
>>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000) 
8.874809793833492 


# this saves us ~5 seconds. Great! 
# let's try the views now 
>>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000) 
0.08828549011070663 

# How about saving another 8.5 seconds? 

Come si può vedere, l'iterazione view oggetto dà una spinta enorme per le prestazioni, riducendo il carico di memoria al contemporaneamente. Dovresti usarli quando devi eseguire operazioni simili a Set.

Nota: Sto correndo su Python 2.7

Problemi correlati