Python: iterando sobre la lista vs sobre la eficacia de los elementos dict

¿La iteración sobre some_dict.items() tan eficiente como la iteración sobre una lista de los mismos elementos en CPython?

Depende de la versión de Python que estés usando. En Python 2, some_dict.items() crea una nueva lista, lo que requiere un tiempo adicional y utiliza memoria adicional. Por otro lado, una vez que se crea la lista, es una lista, por lo que debe tener características de rendimiento idénticas una vez que se complete la sobrecarga de creación de la lista.

En Python 3, some_dict.items() crea un objeto de vista en lugar de una lista, y anticipo que crear e iterar sobre items() sería más rápido que en Python 2, ya que no hay que copiar nada. Pero también anticipo que la iteración sobre una vista ya creada sería un poco más lenta que la iteración sobre una lista ya creada, porque los datos del diccionario se almacenan de forma un poco escasa, y creo que Python no tiene una buena manera de evitar la iteración en cada ubicación. El diccionario, incluso los vacíos.

En Python 2, algunos tiempos confirman mis intuiciones:

 >>> some_dict = dict(zip(xrange(1000), reversed(xrange(1000)))) >>> some_list = zip(xrange(1000), xrange(1000)) >>> %timeit for t in some_list: t 10000 loops, best of 3: 25.6 us per loop >>> %timeit for t in some_dict.items(): t 10000 loops, best of 3: 57.3 us per loop 

Iterar sobre los items es aproximadamente el doble de lento. Usar iteritems es un poco más rápido …

 >>> %timeit for t in some_dict.iteritems(): t 10000 loops, best of 3: 41.3 us per loop 

Pero iterar sobre la lista en sí es básicamente lo mismo que iterar sobre cualquier otra lista:

 >>> some_dict_list = some_dict.items() >>> %timeit for t in some_dict_list: t 10000 loops, best of 3: 26.1 us per loop 

Python 3 puede crear e iterar sobre items más rápido que Python 2 (compare con 57.3 nosotros arriba):

 >>> some_dict = dict(zip(range(1000), reversed(range(1000)))) >>> %timeit for t in some_dict.items(): t 10000 loops, best of 3: 33.4 us per loop 

Pero el tiempo para crear una vista es despreciable; en realidad es más lento iterar más que una lista.

 >>> some_list = list(zip(range(1000), reversed(range(1000)))) >>> some_dict_view = some_dict.items() >>> %timeit for t in some_list: t 10000 loops, best of 3: 18.6 us per loop >>> %timeit for t in some_dict_view: t 10000 loops, best of 3: 33.3 us per loop 

Esto significa que en Python 3, si desea iterar varias veces sobre los elementos de un diccionario, y el rendimiento es crítico, puede obtener una aceleración del 30% almacenando en caché la vista como una lista.

 >>> some_list = list(some_dict_view) >>> %timeit for t in some_list: t 100000 loops, best of 3: 18.6 us per loop 

Un pequeño punto de referencia me muestra que iterar una lista es definitivamente más rápido.

 def iterlist(list_): i = 0 for _ in list_: i += 1 return i def iterdict(dict_): i = 0 for _ in dict_.iteritems(): i += 1 return i def noiterdict(dict_): i = 0 for _ in dict_.items(): i += 1 return i list_ = range(1000000) dict_ = dict(zip(range(1000000), range(1000000))) 

Probado con IPython en Python 2.7 (Kubuntu):

 %timeit iterlist(list_) 10 loops, best of 3: 28.5 ms per loop %timeit iterdict(dict_) 10 loops, best of 3: 39.7 ms per loop %timeit noiterdict(dict_) 10 loops, best of 3: 86.1 ms per loop 

Aunque la iteración a través de some_list es 2 some_dict.items() más some_dict.items() que some_dict.items() , pero la iteración a través de some_list por índice es casi lo mismo que la iteración a través de some_dict por clave.

 K = 1000000 some_dict = dict(zip(xrange(K), reversed(xrange(K)))) some_list = zip(xrange(K), xrange(K)) %timeit for t in some_list: t 10 loops, best of 3: 55.7 ms per loop %timeit for i in xrange(len(some_list)):some_list[i] 10 loops, best of 3: 94 ms per loop %timeit for key in some_dict: some_dict[key] 10 loops, best of 3: 115 ms per loop %timeit for i,t in enumerate(some_list): t 10 loops, best of 3: 103 ms per loop