¿Cuál es la diferencia entre iteradores y generadores? Algunos ejemplos de cuándo usaría cada caso serían útiles.
iterator
es un concepto más general: cualquier objeto cuya clase tiene un next
método ( __next__
en Python 3) y un __iter__
método que sí return self
.
Cada generador es un iterador, pero no al revés. Un generador se construye llamando a una función que tiene una o más yield
expresiones ( yield
declaraciones, en Python 2.5 y anteriores), y es un objeto que cumple con la definición de un párrafo anterior iterator
.
Es posible que desee utilizar un iterador, en lugar de un generador, cuando se necesita una clase con un comportamiento un tanto complejo de mantener el estado, o si quiere exponer a otros métodos además next
(y __iter__
, y __init__
). Muy a menudo, un generador (a veces, para necesidades suficientemente simples, una expresión de generador ) es suficiente, y es más simple de codificar porque el mantenimiento del estado (dentro de límites razonables) es básicamente "hecho por usted" cuando el marco se suspende y se reanuda.
Por ejemplo, un generador como:
def squares(start, stop):
for i in range(start, stop):
yield i * i
generator = squares(a, b)
o la expresión generadora equivalente (genexp)
generator = (i*i for i in range(a, b))
tomaría más código para construir como un iterador personalizado:
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self): return self
def next(self): # __next__ in Python 3
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
iterator = Squares(a, b)
Pero, por supuesto, con la clase Squares
podría ofrecer fácilmente métodos adicionales, es decir
def current(self):
return self.start
si tiene alguna necesidad real de dicha funcionalidad adicional en su aplicación.
for ... in ...:
, pasado a una función, oiter.next()
for..in
sintaxis. Tal vez me faltaba algo, pero fue hace algún tiempo, no recuerdo si lo resolví. ¡Gracias!En resumen: los iteradores son objetos que tienen un método a
__iter__
y a__next__
(next
en Python 2). Los generadores proporcionan una forma fácil e integrada de crear instancias de iteradores.Una función con rendimiento sigue siendo una función que, cuando se llama, devuelve una instancia de un objeto generador:
Una expresión generadora también devuelve un generador:
Para una exposición más profunda y ejemplos, sigue leyendo.
Un generador es un iterador
Específicamente, el generador es un subtipo de iterador.
Podemos crear un generador de varias maneras. Una forma muy común y sencilla de hacerlo es con una función.
Específicamente, una función con rendimiento es una función que, cuando se llama, devuelve un generador:
Y un generador, de nuevo, es un iterador:
Un iterador es un iterable
Un iterador es un Iterable,
que requiere un
__iter__
método que devuelve un iterador:Algunos ejemplos de iterables son las tuplas integradas, listas, diccionarios, conjuntos, conjuntos congelados, cadenas, cadenas de bytes, conjuntos de bytes, rangos y vistas de memoria:
Los iteradores requieren un método
next
o__next__
En Python 2:
Y en Python 3:
Podemos obtener los iteradores de los objetos integrados (u objetos personalizados) con la
iter
función:Se
__iter__
llama al método cuando intenta utilizar un objeto con un ciclo for. Luego__next__
se llama al método en el objeto iterador para obtener cada elemento para el bucle. El iterador se elevaStopIteration
cuando lo ha agotado, y no puede reutilizarse en ese punto.De la documentación
Desde la sección Tipos de generador de la sección Tipos de iterador de la documentación de Tipos incorporados :
(Énfasis añadido.)
Entonces, de esto aprendemos que los generadores son un tipo (conveniente) de iterador.
Ejemplos de objetos iteradores
Puede crear un objeto que implemente el protocolo Iterator creando o extendiendo su propio objeto.
Pero es más fácil simplemente usar un generador para hacer esto:
O quizás más simple, una Expresión de generador (funciona de manera similar a las comprensiones de listas):
Todos se pueden usar de la misma manera:
Conclusión
Puede usar el protocolo Iterator directamente cuando necesite extender un objeto Python como un objeto sobre el que se puede iterar.
Sin embargo, en la gran mayoría de los casos, es mejor usarlo
yield
para definir una función que devuelve un iterador generador o considerar expresiones de generador.Finalmente, tenga en cuenta que los generadores proporcionan aún más funcionalidad como corutinas. Explico los Generadores, junto con la
yield
declaración, en profundidad sobre mi respuesta a "¿Qué hace la palabra clave" rendimiento "?".fuente
Iteradores:
Los iteradores son objetos que utilizan el
next()
método para obtener el siguiente valor de secuencia.Generadores:
Un generador es una función que produce o produce una secuencia de valores utilizando el
yield
método.Cada
next()
llamada al método en el objeto generador (por ejemplo:f
como en el ejemplo a continuación) devuelto por la función del generador (por ejemplo:foo()
función en el ejemplo a continuación), genera el siguiente valor en secuencia.Cuando se llama a una función generadora, devuelve un objeto generador sin siquiera comenzar la ejecución de la función. Cuando
next()
se llama al método por primera vez, la función comienza a ejecutarse hasta que alcanza la declaración de rendimiento que devuelve el valor obtenido. El rendimiento realiza un seguimiento, es decir, recuerda la última ejecución. Y la segundanext()
llamada continúa desde el valor anterior.El siguiente ejemplo demuestra la interacción entre el rendimiento y la llamada al siguiente método en el objeto generador.
fuente
Agregar una respuesta porque ninguna de las respuestas existentes aborda específicamente la confusión en la literatura oficial.
Las funciones generadoras son funciones ordinarias definidas usando en
yield
lugar dereturn
. Cuando se llama, una función generadora devuelve un objeto generador , que es un tipo de iterador, tiene unnext()
método. Cuando llamanext()
, se devuelve el siguiente valor producido por la función del generador.La función o el objeto pueden llamarse "generador" dependiendo del documento fuente de Python que lea. El glosario de Python dice funciones generadoras, mientras que la wiki de Python implica objetos generadores. El tutorial de Python logra notablemente implicar ambos usos en el espacio de tres oraciones:
Las primeras dos oraciones identifican generadores con funciones generadoras, mientras que la tercera oración los identifica con objetos generadores.
A pesar de toda esta confusión, uno puede buscar la referencia del lenguaje Python para la palabra clara y final:
Entonces, en un uso formal y preciso, "generador" no calificado significa objeto generador, no función generador.
Las referencias anteriores son para Python 2 pero la referencia del lenguaje Python 3 dice lo mismo. Sin embargo, el glosario de Python 3 establece que
fuente
Todos tienen una respuesta realmente agradable y detallada con ejemplos y realmente lo aprecio. Solo quería dar unas pocas líneas de respuesta para las personas que aún no tienen una idea conceptual clara:
Si crea su propio iterador, es un poco complicado: debe crear una clase y al menos implementar el iter y los siguientes métodos. Pero, ¿qué pasa si no quieres pasar por esta molestia y quieres crear rápidamente un iterador? Afortunadamente, Python proporciona una forma abreviada para definir un iterador. Todo lo que necesita hacer es definir una función con al menos 1 llamada para ceder y ahora, cuando llame a esa función, devolverá " algo " que actuará como un iterador (puede llamar al siguiente método y usarlo en un bucle for). Este algo tiene un nombre en Python llamado Generador
Espero que eso aclare un poco.
fuente
Las respuestas anteriores omitieron esta adición: un generador tiene un
close
método, mientras que los iteradores típicos no. Elclose
método desencadena unaStopIteration
excepción en el generador, que puede quedar atrapado en unafinally
cláusula en ese iterador, para tener la oportunidad de ejecutar una limpieza. Esta abstracción lo hace más utilizable en iteradores grandes que simples. Uno puede cerrar un generador como podría cerrar un archivo, sin tener que preocuparse por lo que hay debajo.Dicho esto, mi respuesta personal a la primera pregunta sería: iteratable solo tiene un
__iter__
método, los iteradores típicos solo tienen un__next__
método, los generadores tienen tanto an__iter__
como ay__next__
adicionalclose
.Para la segunda pregunta, mi respuesta personal sería: en una interfaz pública, tiendo a favorecer mucho a los generadores, ya que es más resistente: el
close
método es más fácil de componeryield from
. A nivel local, puedo usar iteradores, pero solo si es una estructura plana y simple (los iteradores no se componen fácilmente) y si hay razones para creer que la secuencia es bastante corta, especialmente si se puede detener antes de llegar al final. Tiendo a ver los iteradores como una primitiva de bajo nivel, excepto como literales.Para el flujo de control, los generadores son un concepto tan importante como las promesas: ambos son abstractos y componibles.
fuente
__iter__
método, ¿cómo es que un iterador__next__
solo puede tenerlo ? Si se supone que son iterables, esperaría que necesariamente lo tengan__iter__
también.__iter__
en iterables para devolver un iterador, que solo requiere unnext
método (__next__
en Python3). No confunda los estándares (para escribir en un pato) con su implementación (cómo lo implementó un intérprete de Python en particular). Esto es un poco como la confusión entre las funciones del generador (definición) y los objetos del generador (implementación). ;)Una función de generador es como una función normal en Python, pero contiene una o más
yield
declaraciones. Las funciones de generador son una gran herramienta para crear objetos Iterator lo más fácil posible. El objeto iterador returend por función de generador también se denomina objeto generador o generador .En este ejemplo, he creado una función Generador que devuelve un objeto Generador
<generator object fib at 0x01342480>
. Al igual que otros iteradores, los objetos Generator pueden usarse en unfor
bucle o con la función incorporadanext()
que devuelve el siguiente valor del generador.Entonces, una función generadora es la forma más fácil de crear un objeto Iterator.
Cada objeto generador es un iterador pero no al revés. Se puede crear un objeto iterador personalizado si su clase implementa
__iter__
y__next__
método (también llamado protocolo iterador).Sin embargo, es mucho más fácil usar la función de generadores para crear iteradores porque simplifican su creación, pero un Iterador personalizado le brinda más libertad y también puede implementar otros métodos de acuerdo con sus requisitos, como se muestra en el siguiente ejemplo.
fuente
Ejemplos de Ned Batchelder muy recomendados para iteradores y generadores.
Un método sin generadores que hace algo a los números pares
mientras usando un generador
return
declaraciónLlamar al
evens
método (generador) es como siempreIterador
y este marcador no tiene nada que hacer excepto moverse
next
Para usar Generator ... necesitamos una función
Para usar Iterator ... necesitamos
next
yiter
Como se ha dicho:
Todo el beneficio de Iterator:
fuente
Puede comparar ambos enfoques para los mismos datos:
Además, si verifica la huella de la memoria, el generador toma mucha menos memoria ya que no necesita almacenar todos los valores en la memoria al mismo tiempo.
fuente
Estoy escribiendo específicamente para los novatos de Python de una manera muy simple, aunque en el fondo Python hace muchas cosas.
Comencemos con lo muy básico:
Considera una lista,
Escribamos una función equivalente:
o / p de
print(l): [1,2,3]
& o / p deprint(f()) : [1,2,3]
Hagamos que la lista sea iterable: en Python, la lista siempre es iterable, lo que significa que puede aplicar el iterador cuando lo desee.
Apliquemos el iterador en la lista:
Hagamos que una función sea iterable, es decir, escriba una función generadora equivalente. En python tan pronto como introduzca la palabra clave
yield
; se convierte en una función generadora y el iterador se aplicará implícitamente.Nota: Cada generador siempre es iterable con el iterador implícito aplicado y aquí el iterador implícito es el punto crucial. Por lo tanto, la función del generador será:
Entonces, si has observado, tan pronto como hiciste la función fa generador, ya es iter (f)
Ahora,
Es como si estuvieras convirtiendo int a int (x) que ya es int y seguirá siendo int (x).
Por ejemplo o / p de:
es
Nunca olvides que esto es Python y no C o C ++
Por lo tanto, la conclusión de la explicación anterior es:
fuente