2012-01-21 8 views
121

En python 2.7, tenemos el dictionary view methods disponible.¿Qué son los objetos de vista del diccionario?

Ahora, sé que los pros y los contras de los siguientes:

  • dict.items() (y values, keys): devuelve una lista, por lo que en realidad se puede almacenar el resultado
  • dict.iteritems() (y similares) : devuelve un generador, por lo que puede iterable sobre cada valor generado uno por uno.

¿Qué son dict.viewitems() (y similares) para? ¿Cuáles son sus beneficios? ¿Como funciona? ¿Qué es una vista después de todo?

He leído que la vista siempre refleja los cambios del diccionario. Pero, ¿cómo se comporta desde el punto de vista del rendimiento y la memoria? ¿Cuáles son los pro y los contras?

Respuesta

118

Las vistas de diccionario son esencialmente lo que su nombre dice: vistas son simplemente como una ventana en las claves y valores (o elementos) de un diccionario. Aquí es un extracto de la official documentation para 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]) 

(El Python 2 equivalente utiliza dishes.viewkeys() y dishes.viewvalues().)

Este ejemplo muestra el carácter dinámico de puntos de vista: las teclas de vista es no una copia de las claves en un punto determinado en el tiempo, sino una ventana simple que le muestra las teclas; si se cambian, lo que ves a través de la ventana también cambia. Esta característica puede ser útil en algunas circunstancias (por ejemplo, uno puede trabajar con una vista de las teclas en varias partes de un programa en lugar de volver a calcular la lista actual de claves cada vez que las necesite).

Una ventaja es que mirando en, por ejemplo, las claves sólo utiliza un pequeño y fijo cantidad de memoria y requiere una pequeña cantidad fija de tiempo de procesador, ya que no hay creación de una lista de claves (Python 2, por otro lado, a menudo crea innecesariamente una nueva lista, citada por Rajendran T, que toma la memoria y el tiempo en una cantidad proporcional a la longitud de la lista). Para continuar con la analogía de la ventana, si quiere ver un paisaje detrás de una pared, simplemente haga una abertura (construya una ventana); copiar las claves en una lista correspondería a una pintura del paisaje en su pared, en su lugar, la copia toma tiempo, espacio y no se actualiza.

En resumen, las vistas son simplemente ... vistas (ventanas) en su diccionario, que muestran los contenidos del diccionario, incluso después de que cambie. Ofrecen características que difieren de las de las listas: una lista de claves contiene una copia de las teclas del diccionario en un punto dado en el tiempo, mientras que una vista es dinámica y es mucho más rápida de obtener, ya que no tiene que copiar ninguna datos (claves o valores) para poder ser creados.

+5

+1. Ok, ¿cómo difiere eso de tener acceso directo a la lista interna de claves? ¿Es eso más rápido, más lento? Más memoria eficiente? Restringido? Si puede leerlo y editarlo, se siente exactamente lo mismo que tener una referencia a esta lista. –

+2

Gracias. Lo que sucede es que las vistas * son * su acceso a "la lista interna de claves" (tenga en cuenta que esta "lista de claves" no es una lista de Python, pero es precisamente una vista). Las vistas son más eficientes en cuanto a la memoria que las listas de claves (o valores o elementos) de Python 2, ya que no copian nada; en realidad son como "una referencia a la lista de claves" (nótese también que "una referencia a una lista" simplemente se llama una lista, en Python, ya que las listas son objetos mutables). También tenga en cuenta que no puede editar las vistas directamente: en su lugar, aún edita el diccionario, y las vistas reflejan sus cambios de inmediato. – EOL

+2

Ok, todavía no tengo clara la implementación, pero hasta ahora es la mejor respuesta. –

13

Los métodos de vista devuelven una lista (no una copia de la lista, comparada con .keys(), .items() y .values()), por lo que es más liviana, pero refleja los contenidos actuales del diccionario.

De Python 3.0 - dict methods return views - why?

La razón principal es que para muchos casos de uso que regresan una lista completamente unifamiliar es innecesario y derrochador. Se requerirá copiar todo el contenido (que puede o no ser mucho).

Si simplemente desea iterar sobre las teclas, no es necesario crear una nueva lista . Y si realmente lo necesita como una lista separada (como una copia ), entonces puede crear fácilmente esa lista desde la vista.

+2

1, pero aún no está claro qué vista es y cómo funciona. –

+4

Los métodos de vista devuelven objetos de vista, que no se ajustan a la interfaz de lista. –

16

Sólo a partir de la lectura de los documentos consigo esta impresión:

  1. Las vistas son "pseudo-set-like", ya que no son compatibles con la indexación, así que lo que puede hacer con ellos es prueba para la membresía y repetir sobre ellos (porque las claves son manejables y únicas, las vistas de teclas y elementos son más "similares" porque no contienen duplicados).
  2. Puede almacenarlos y usarlos varias veces, al igual que las versiones de la lista.
  3. Dado que reflejan el diccionario subyacente, cualquier cambio en el diccionario cambiará la vista, y cambiará seguramente el orden de la iteración. Entonces, a diferencia de las versiones de la lista, no son "estables".
  4. Debido a que reflejan el diccionario subyacente, casi con seguridad son pequeños objetos proxy; copiar las claves/valores/elementos requeriría que vean el diccionario original de alguna manera y que lo copien varias veces cuando ocurran cambios, lo que sería una implementación absurda. Así que esperaría muy poca sobrecarga de memoria, pero el acceso sería un poco más lento que directamente al diccionario.

Supongo que el uso de la clave es si mantiene un diccionario alrededor e itera repetidamente sobre sus claves/elementos/valores con modificaciones intermedias. Puede usar una vista en su lugar, convirtiendo for k, v in mydict.iteritems(): en for k, v in myview:. Pero si solo está iterando sobre el diccionario una vez, creo que las versiones iter todavía son preferibles.

+2

+1 para analizar los pro y los contras de las pocas informaciones que obtuvimos. –

+0

Si creo un iterador sobre una vista, aún se invalida cada vez que cambia el diccionario. Ese es el mismo problema que con un iterador sobre el diccionario mismo (por ejemplo, 'iteritems()'). Entonces, ¿cuál es el punto de estos puntos de vista? ¿Cuándo estoy feliz de tenerlos? – Alfe

+0

@Alfe Tienes razón, ese es un problema con la iteración del diccionario y las vistas no ayudan en absoluto. Supongamos que necesita pasar los valores de un diccionario a una función. Podría usar '.values ​​()', pero eso implica hacer una copia completa como una lista, lo que podría ser costoso. Hay '.itervalues ​​()' pero no puede consumirlos más de una vez, por lo que no funcionará con todas las funciones. Las vistas no requieren una copia costosa, pero siguen siendo más útiles como valor independiente que un iterador. Pero todavía no están destinados a ayudar con la iteración y la modificación al mismo tiempo (allí realmente quieres una copia). – Ben

16

Como mencionó dict.items() devuelve una copia de la lista del diccionario de pares (clave, valor) que es un desperdicio y dict.iteritems() devuelve un iterador sobre los pares del diccionario (clave, valor).

Ahora toma el siguiente ejemplo para ver la diferencia entre un interator de dict y 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 

Mientras que un punto de vista simplemente le muestra lo que está en el dict. No importa si ha cambiado:

>>> 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 es simplemente como se ve el diccionario ahora. Después de eliminar una entrada, .items() hubiera estado desactualizado y .iteritems() habría arrojado un error.

+0

Gran ejemplo, gracias. Sin embargo, debe ser v = d.items() no v - d.viewitems() – rix

+0

La pregunta es sobre Python 2.7, por lo que 'viewitems()' es realmente correcto ('items()' da una vista correctamente en Python ** 3 **). – EOL

+0

Sin embargo, una vista ** no puede ** usarse para iterar sobre un diccionario mientras se modifica. –

0

Las vistas le permiten acceder a la estructura de datos subyacente, sin copiarla. Además de ser dinámico en lugar de crear una lista, uno de sus usos más útiles es la prueba in. Digamos que quiere verificar si un valor está en el dict o no (ya sea la clave o el valor).

La primera opción es crear una lista de las teclas usando dict.keys(), esto funciona, pero obviamente consume más memoria. Si el dict es muy grande? Eso sería un desperdicio.

Con views puede iterar la estructura de datos real, sin lista intermedia.

Vamos a usar ejemplos. Tengo un diccionario con 1.000 llaves de cadenas aleatorias y dígitos y k es la clave que quiero buscar

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? 

Como se puede ver, la iteración view objeto da un gran impulso para el rendimiento, reduciendo la sobrecarga de la memoria en el Mismo tiempo. Debe usarlos cuando necesite realizar Set operaciones similares.

Nota: Estoy corriendo en Python 2.7

Cuestiones relacionadas