speed optimize loop iteritems for code python performance iterator

python - optimize - ¿Ventajas de rendimiento para los iteradores?



optimize for loop python (9)

Las ventajas de rendimiento (si las hay) se ofrecen mediante el uso de iteradores. Parece ser el ''Camino correcto'' para resolver muchos problemas, pero ¿crea un código más rápido / más consciente de la memoria? Estoy pensando específicamente en Python, pero no restrinja las respuestas a eso.


El principal beneficio de los iteradores no es el rendimiento. En mi experiencia, la solución más eficaz es crear un algoritmo que incruste la estructura de datos que elija. El beneficio de los iteradores es que le permiten desacoplar los datos y el algoritmo y, por lo tanto, generalizar y reutilizar ambos. Si esto también se puede hacer sin (o con poca) degradación del rendimiento, entonces es una ganancia neta.

Mi ejemplo favorito de uso de iterador se puede encontrar en la Biblioteca de plantillas estándar de C ++. Se las arregla para demostrar el poder y la belleza de la abstracción al separar limpiamente el contenedor y el algoritmo sin sacrificar el rendimiento. La comprensión de este diseño tuvo un profundo efecto en la forma en que pienso sobre el código.


En realidad, hay un muy buen correo en la lista de correo de python sobre esto: Iterators vs Lists . Es un poco anticuado (de 2003), pero por lo que sé, sigue siendo válido.

Aquí está el resumen:

Para conjuntos de datos pequeños, los enfoques basados ​​en iteradores y listas tienen un rendimiento similar. Para conjuntos de datos más grandes, los iteradores ahorran tiempo y espacio.

Lo que obtendría de esto es esto: los iteradores deben ser preferidos a cargar datos en una lista si es posible. Pero a menos que tenga un gran conjunto de datos, no contorsione su código para hacer algo que debería encajar en una lista para trabajar con un iterador.


Hay una respuesta que creo que confunde un poco el concepto de generador e iterador. Así que decidí intentar responder esta pregunta con un ejemplo de metáfora.

Estoy trabajando en una cocina, mi jefe me dio la tarea de sumar el peso de 10 (o 100 o un millón) de panes. Tengo una balanza y una calculadora (trucos de magia de mi algoritmo). A continuación se muestran el objeto iterable, generador, iterador, diferencia de enfoque:

  1. Objeto iterable: Cada pan se almacena en una caja (memoria), peso el primer (o el 0º) pan, pongo su peso, y devuelvo el pan a la caja, luego voy a la siguiente, lo peso y pongo de vuelta, una y otra vez, etc., etc. Al final, obtuve el peso total y los 10 (100 o millones) de pan todavía están allí en sus cajas.

  2. Generador: no hay suficientes cajas para almacenar todo este pan, así que pedí la ayuda de un panadero (el generador), él hace el primer pan, me lo da, lo pesé, lo coloqué, lo arrojé. de distancia y pídale otra, una y otra vez, etc., hasta que consiga el último pan (o tal vez el panadero se quede sin harina). Al final, tengo el resultado, ninguno de los panes está ahí. Pero a quién le importa, mi jefe solo me pide que pese estos panes, él no dijo que no los puedo tirar (qué brillante ayudante de camarero).

  3. Iterador: Le pido a alguien (iterador) que me ayude a mover el primer pan a la báscula, lo pesé y puse el resultado. Este alguien iría a buscar el siguiente para medir, una y otra vez, etc. Realmente no tengo idea si alguien (iterador) obtiene el pan de una caja o de un panadero. Eventualmente, tengo el peso total, no me importa.

De todos modos, para resumir:

  1. El objeto que se puede reutilizar necesita algo de memoria para almacenar datos para comenzar. Al final, los datos todavía están allí.

  2. El generador no necesita memoria para almacenar datos, para empezar, genera datos sobre la marcha.

  3. Iterador es un canal entre el algoritmo y sus datos. Es posible que estos datos ya hayan estado allí y se hayan almacenado en la memoria o se puedan generar sobre la marcha mediante un generador. En el primer caso, esa memoria se liberaría poco a poco a medida que el iterador se mantiene iterando. Así que estoy de acuerdo con la respuesta anterior de que el iterador es bueno debido a su abstracción que permite el aislamiento del algoritmo y los datos.

Python no funciona exactamente así. Espero que ayude a aclarar un poco.


Los iteradores son solo clases que implementan una interfaz particular , específicamente una interfaz para ir a la siguiente . En Python, las listas, las tuplas, los dados, las cadenas y los archivos implementan esta interfaz. Si se implementan de manera deficiente, puede resultar en un rendimiento deficiente, pero no hay nada inherente a la interfaz que implique un rendimiento bueno o malo.


Mi inferencia de muchas de las respuestas anteriores es "Use la lista para codificar. Si es necesario, vuelva a factorizar los iteradores" La diferencia no es aparente a menos que tenga un conjunto de datos grande.

Otra cosa a tener en cuenta es que, incluso cuando se usan listas a menudo, el conjunto de datos con el que estamos operando es cada vez más pequeño y más pequeño.


Para respaldar la respuesta de @Christian Witts :

range vs. rendimiento de range

python25 -mtimeit "for i in xrange(1000): pass" 10000 loops, best of 3: 56.3 usec per loop python25 -mtimeit "for i in range(1000): pass" 10000 loops, best of 3: 80.9 usec per loop python26 -mtimeit "for i in xrange(1000): pass" 10000 loops, best of 3: 48.8 usec per loop python26 -mtimeit "for i in range(1000): pass" 10000 loops, best of 3: 68.6 usec per loop

por cierto, ni range() ni xrange() son iteradores:

>>> hasattr(range(1), ''next'') False >>> hasattr(xrange(1), ''next'') False >>> iter(xrange(1)) <rangeiterator object at 0x0097A500> >>> iter(range(1)) <listiterator object at 0x00A7BFD0> >>> iter([]) <listiterator object at 0x00A7BE30> >>> iter(i for i in (1,)) <generator object at 0x00A7F940> >>> (i for i in (1,)) <generator object at 0x00A7FDC8>


Un iterador es simplemente un objeto que proporciona métodos para permitir el desplazamiento a través de una colección. Podría atravesar todos los elementos de una matriz o todos los nodos de un árbol con la misma interfaz. Los árboles y las matrices son estructuras de datos muy diferentes y requieren diferentes métodos para atravesar ... pero con un iterador puedes recorrer todos los elementos de la misma manera.

Para un tipo de colección, también puede haber diferentes formas de recorrerla y una sola colección podría tener múltiples iteradores. Podría tener un primer iterador de profundidad o un iterador de primer ancho que atraviese una estructura de árbol y devuelva los nodos en diferentes órdenes. . Los iteradores no están diseñados para el rendimiento ... sino que suelen proporcionar una interfaz consistente para atravesar estructuras.


Un poco fuera de tema, pero agrega más peso al uso de listas sobre iteradores en general: con los iteradores es más fácil tener efectos secundarios, considere esto:

def foo(arg: Iterable[str]): print(list(arg)) # side effect: arg is exhausted at this point ...

Se puede decir que las pruebas deben captar esto, pero a veces no es así. Las listas no tienen este problema ya que son apátridas (en el sentido de iteración).


Para Python, los generadores serán más rápidos y tendrán una mejor eficiencia de memoria. Solo piense en un ejemplo de range(1000) vs xrange(1000) (Esto se ha cambiado en 3.0, el rango ahora es un generador). Con Rango, usted precompila su lista, pero XRange solo tiene un objeto generador y produce el siguiente elemento cuando es necesario.

La diferencia de rendimiento no es grande en cosas pequeñas, pero tan pronto como comience a utilizarlas cada vez más y más grandes conjuntos de información lo notará rápidamente. Además, no solo tiene que generar y luego pasar, sino que también consumirá memoria adicional para su elemento preconstruido en el que, al igual que con la expresión del generador, solo se crea un elemento a la vez.