Unterschied zwischen Pythons Generatoren und Iteratoren

538

Was ist der Unterschied zwischen Iteratoren und Generatoren? Einige Beispiele, wann Sie jeden Fall verwenden würden, wären hilfreich.

newToProgramming
quelle

Antworten:

543

iteratorist ein allgemeineres Konzept: Jedes Objekt, dessen Klasse eine nextMethode ( __next__in Python 3) und eine __iter__Methode hat, die dies tut return self.

Jeder Generator ist ein Iterator, aber nicht umgekehrt. Ein Generator wird durch Aufrufen einer Funktion erstellt, die einen oder mehrere yieldAusdrücke enthält ( yieldAnweisungen in Python 2.5 und früheren Versionen) und ein Objekt ist, das der Definition von a im vorherigen Absatz entspricht iterator.

Möglicherweise möchten Sie einen benutzerdefinierten Iterator anstelle eines Generators verwenden, wenn Sie eine Klasse mit einem etwas komplexen Verhalten zur Aufrechterhaltung des Zustands benötigen oder andere Methoden als next(und __iter__und __init__) verfügbar machen möchten . Meistens ein Generator (manchmal für hinreichend einfache Bedürfnisse, ein Generator Ausdruck ) ist ausreichend, und es ist einfacher Code , weil Zustand Wartung (in Grenzen) ist im Grunde „für Sie getan“ durch den Rahmen aufgehängt zu werden und wieder aufgenommen.

Zum Beispiel ein Generator wie:

def squares(start, stop):
    for i in range(start, stop):
        yield i * i

generator = squares(a, b)

oder der äquivalente Generatorausdruck (genexp)

generator = (i*i for i in range(a, b))

würde mehr Code benötigen, um als benutzerdefinierter Iterator erstellt zu werden:

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)

Aber natürlich können Sie mit dem Unterricht Squaresleicht zusätzliche Methoden anbieten, z

    def current(self):
       return self.start

wenn Sie tatsächlich solche zusätzlichen Funktionen in Ihrer Anwendung benötigen.

Alex Martelli
quelle
Wie verwende ich den Iterator, nachdem ich ihn erstellt habe?
Vincenzooo
@ Vincentenzooo das hängt davon ab, was du damit machen willst. Es wird entweder Teil for ... in ...:einer Funktion sein, an eine Funktion übergeben, oder Sie rufen aniter.next()
Caleth
@Caleth Ich habe nach der genauen Syntax gefragt, weil beim Versuch, die for..inSyntax zu verwenden, ein Fehler aufgetreten ist . Vielleicht hat mir etwas gefehlt, aber es ist einige Zeit her, ich erinnere mich nicht, ob ich es gelöst habe. Vielen Dank!
Vincenzooo
136

Was ist der Unterschied zwischen Iteratoren und Generatoren? Einige Beispiele, wann Sie jeden Fall verwenden würden, wären hilfreich.

Zusammenfassend: Iteratoren sind Objekte mit einem __iter__und einem __next__(next in Python 2) Methode. Generatoren bieten eine einfache, integrierte Möglichkeit, Instanzen von Iteratoren zu erstellen.

Eine Funktion mit Yield darin ist immer noch eine Funktion, die beim Aufruf eine Instanz eines Generatorobjekts zurückgibt:

def a_function():
    "when called, returns generator object"
    yield

Ein Generatorausdruck gibt auch einen Generator zurück:

a_generator = (i for i in range(0))

Lesen Sie weiter, um eine ausführlichere Darstellung und Beispiele zu erhalten.

Ein Generator ist ein Iterator

Insbesondere ist der Generator ein Subtyp des Iterators.

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True

Wir können einen Generator auf verschiedene Arten erstellen. Ein sehr häufiger und einfacher Weg, dies zu tun, ist eine Funktion.

Insbesondere ist eine Funktion mit Ausbeute eine Funktion, die beim Aufruf einen Generator zurückgibt:

>>> def a_function():
        "just a function definition with yield in it"
        yield
>>> type(a_function)
<class 'function'>
>>> a_generator = a_function()  # when called
>>> type(a_generator)           # returns a generator
<class 'generator'>

Und ein Generator ist wieder ein Iterator:

>>> isinstance(a_generator, collections.Iterator)
True

Ein Iterator ist ein Iterator

Ein Iterator ist ein Iterator,

>>> issubclass(collections.Iterator, collections.Iterable)
True

__iter__Dies erfordert eine Methode, die einen Iterator zurückgibt:

>>> collections.Iterable()
Traceback (most recent call last):
  File "<pyshell#79>", line 1, in <module>
    collections.Iterable()
TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__

Einige Beispiele für Iterables sind die integrierten Tupel, Listen, Wörterbücher, Mengen, eingefrorenen Mengen, Zeichenfolgen, Byte-Zeichenfolgen, Byte-Arrays, Bereiche und Speicheransichten:

>>> all(isinstance(element, collections.Iterable) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

Iteratoren erfordern ein nextoder__next__ Methode

In Python 2:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<pyshell#80>", line 1, in <module>
    collections.Iterator()
TypeError: Can't instantiate abstract class Iterator with abstract methods next

Und in Python 3:

>>> collections.Iterator()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__

Wir können die Iteratoren von den eingebauten Objekten (oder benutzerdefinierten Objekten) mit der folgenden iterFunktion abrufen:

>>> all(isinstance(iter(element), collections.Iterator) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True

Die __iter__Methode wird aufgerufen, wenn Sie versuchen, ein Objekt mit einer for-Schleife zu verwenden. Dann wird die __next__Methode für das Iteratorobjekt aufgerufen, um jedes Element für die Schleife herauszuholen. Der Iterator wird ausgelöst, StopIterationwenn Sie ihn erschöpft haben, und er kann zu diesem Zeitpunkt nicht wiederverwendet werden.

Aus der Dokumentation

Aus dem Abschnitt "Generatortypen" des Abschnitts "Iteratortypen" der Dokumentation zu integrierten Typen :

Die Generatoren von Python bieten eine bequeme Möglichkeit, das Iteratorprotokoll zu implementieren. Wenn die __iter__()Methode eines Containerobjekts als Generator implementiert ist, gibt sie automatisch ein Iteratorobjekt (technisch gesehen ein Generatorobjekt) zurück, das die Methoden __iter__()und next()[ __next__()in Python 3] bereitstellt. Weitere Informationen zu Generatoren finden Sie in der Dokumentation zum Ertragsausdruck.

(Betonung hinzugefügt.)

Daraus lernen wir, dass Generatoren eine (bequeme) Art von Iterator sind.

Beispiel für Iteratorobjekte

Sie können ein Objekt erstellen, das das Iterator-Protokoll implementiert, indem Sie ein eigenes Objekt erstellen oder erweitern.

class Yes(collections.Iterator):

    def __init__(self, stop):
        self.x = 0
        self.stop = stop

    def __iter__(self):
        return self

    def next(self):
        if self.x < self.stop:
            self.x += 1
            return 'yes'
        else:
            # Iterators must raise when done, else considered broken
            raise StopIteration

    __next__ = next # Python 3 compatibility

Es ist jedoch einfacher, einfach einen Generator zu verwenden, um dies zu tun:

def yes(stop):
    for _ in range(stop):
        yield 'yes'

Oder vielleicht einfacher, ein Generatorausdruck (funktioniert ähnlich wie Listenverständnisse):

yes_expr = ('yes' for _ in range(stop))

Sie können alle auf die gleiche Weise verwendet werden:

>>> stop = 4             
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop), 
                             ('yes' for _ in range(stop))):
...     print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...     
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes

Fazit

Sie können das Iterator-Protokoll direkt verwenden, wenn Sie ein Python-Objekt als ein Objekt erweitern müssen, über das iteriert werden kann.

In den allermeisten Fällen können Sie jedoch am besten yieldeine Funktion definieren, die einen Generator-Iterator zurückgibt, oder Generator-Ausdrücke berücksichtigen.

Beachten Sie schließlich, dass Generatoren als Coroutinen noch mehr Funktionen bieten. Ich erkläre Generatoren zusammen mit der yieldAussage ausführlich in meiner Antwort auf "Was macht das Schlüsselwort" Yield "?".

Aaron Hall
quelle
41

Iteratoren:

Iterator sind Objekte, die verwendet werden next() Methode verwenden, um den nächsten Wert der Sequenz zu erhalten.

Generatoren:

Ein Generator ist eine Funktion, die mit eine Folge von Werten erzeugt oder liefert yield Methode .

Jeder next() Methodenaufruf für das Generatorobjekt (z. fB. wie im folgenden Beispiel), der von der Generatorfunktion zurückgegeben wird (z. foo()B. Funktion im folgenden Beispiel), generiert den nächsten Wert nacheinander.

Wenn eine Generatorfunktion aufgerufen wird, gibt sie ein Generatorobjekt zurück, ohne mit der Ausführung der Funktion zu beginnen. Wannnext() Methode zum ersten Mal aufgerufen wird, wird die Funktion ausgeführt, bis sie die Yield-Anweisung erreicht, die den Yield-Wert zurückgibt. Die Ausbeute verfolgt, dh erinnert sich an die letzte Ausführung. Der zweite next()Aufruf wird vom vorherigen Wert fortgesetzt.

Das folgende Beispiel zeigt das Zusammenspiel zwischen Ausbeute und Aufruf der nächsten Methode für das Generatorobjekt.

>>> def foo():
...     print "begin"
...     for i in range(3):
...         print "before yield", i
...         yield i
...         print "after yield", i
...     print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0            # Control is in for loop
0
>>> f.next()
after yield 0             
before yield 1            # Continue for loop
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

quelle
3
Nur FYI Rendite ist keine Methode, es ist Schlüsselwort
Jay Parikh
25

Hinzufügen einer Antwort, da keine der vorhandenen Antworten speziell die Verwirrung in der offiziellen Literatur anspricht.

Generatorfunktionen sind gewöhnliche Funktionen, die mityieldanstelle vondefiniert werdenreturn. Beim Aufruf gibt eine Generatorfunktion ein Generatorobjekt zurück , das eine Art Iterator ist - es hat einenext()Methode. Wenn du anrufstnext() , wird der nächste von der Generatorfunktion ausgegebene Wert zurückgegeben.

Je nachdem, welches Python-Quelldokument Sie lesen, kann entweder die Funktion oder das Objekt als "Generator" bezeichnet werden. Das Python-Glossar besagt, dass Generatorfunktionen vorhanden sind, während das Python-Wiki Generatorobjekte impliziert. Das Python-Tutorial schafft es bemerkenswerterweise, beide Verwendungen innerhalb von drei Sätzen zu implizieren :

Generatoren sind ein einfaches und leistungsstarkes Werkzeug zum Erstellen von Iteratoren. Sie sind wie reguläre Funktionen geschrieben, verwenden jedoch die Yield-Anweisung, wenn sie Daten zurückgeben möchten. Bei jedem Aufruf von next () wird der Generator dort fortgesetzt, wo er aufgehört hat (er merkt sich alle Datenwerte und welche Anweisung zuletzt ausgeführt wurde).

Die ersten beiden Sätze identifizieren Generatoren mit Generatorfunktionen, während der dritte Satz sie mit Generatorobjekten identifiziert.

Trotz all dieser Verwirrung kann man die Python-Sprachreferenz für das klare und endgültige Wort suchen :

Der Ertragsausdruck wird nur beim Definieren einer Generatorfunktion verwendet und kann nur im Hauptteil einer Funktionsdefinition verwendet werden. Die Verwendung eines Ertragsausdrucks in einer Funktionsdefinition reicht aus, um zu bewirken, dass diese Definition eine Generatorfunktion anstelle einer normalen Funktion erstellt.

Wenn eine Generatorfunktion aufgerufen wird, gibt sie einen Iterator zurück, der als Generator bezeichnet wird. Dieser Generator steuert dann die Ausführung einer Generatorfunktion.

In der formalen und präzisen Verwendung bedeutet "Generator" unqualifiziert also Generatorobjekt, nicht Generatorfunktion.

Die obigen Referenzen gelten für Python 2, aber die Python 3-Sprachreferenz sagt dasselbe. Das Python 3-Glossar besagt dies jedoch

Generator ... Bezieht sich normalerweise auf eine Generatorfunktion, kann sich jedoch in einigen Kontexten auf einen Generatoriterator beziehen. In Fällen, in denen die beabsichtigte Bedeutung nicht klar ist, werden durch die Verwendung der vollständigen Begriffe Mehrdeutigkeiten vermieden.

Paul
quelle
Ich glaube nicht, dass es viel Verwirrung zwischen Generatorfunktionen und Generatorobjekten gibt, aus dem gleichen Grund gibt es normalerweise keine Verwirrung zwischen Klassen und ihren Instanzen. In beiden Fällen rufen Sie einen an, um den anderen zu erhalten, und in gelegentlichen Gesprächen (oder schnell geschriebenen Dokumentationen) können Sie den Klassennamen oder das Wort "Generator" für beide verwenden. Sie müssen nur in seltenen Situationen explizit über "Generatorfunktion" und "Generatorobjekt" sprechen, in denen es um diejenige geht, über die Sie sprechen.
Blckknght
6
1. Unabhängig von theoretischen Gründen, warum es keine Verwirrung geben sollte, leugnen und widersprechen sich Kommentare zu anderen Antworten auf diese Frage ohne Lösung, was darauf hinweist, dass tatsächliche Verwirrung besteht. 2. Zufällige Ungenauigkeiten sind in Ordnung, aber eine präzise, ​​maßgebliche Quelle sollte mindestens eine der Optionen für SO sein. In meinem aktuellen Projekt verwende ich sowohl Generatorfunktionen als auch Objekte ausgiebig, und die Unterscheidung ist beim Entwerfen und Codieren sehr wichtig. Es ist gut zu wissen, welche Terminologie jetzt verwendet werden soll, damit ich später nicht Dutzende von Variablennamen und Kommentaren ändern muss.
Paul
2
Stellen Sie sich eine mathematische Literatur vor, in der nicht zwischen einer Funktion und ihrem Rückgabewert unterschieden wird. Es ist gelegentlich bequem, sie informell zusammenzuführen, aber es erhöht das Risiko einer Vielzahl von Fehlern. Fortgeschrittene moderne Mathematik würde erheblich und unnötig behindert, wenn die Unterscheidung nicht in Konvention, Sprache und Notation formalisiert würde.
Paul
2
Funktionen höherer Ordnung, die Generatoren oder Generatorfunktionen herumgeben, mögen seltsam klingen, aber für mich sind sie aufgetaucht. Ich arbeite in Apache Spark und es erzwingt einen sehr funktionalen Programmierstil. Die Funktionen müssen alle Arten von Objekten erstellen, übergeben und weitergeben, um Dinge zu erledigen. Ich hatte eine Reihe von Situationen, in denen ich den Überblick darüber verlor, mit welcher Art von "Generator" ich arbeitete. Hinweise in Variablennamen und Kommentaren unter Verwendung der konsistenten und korrekten Terminologie halfen, die Verwirrung zu beseitigen. Die Dunkelheit eines Pythonisten kann das Zentrum des Projektdesigns eines anderen sein!
Paul
1
@ Paul, danke, dass du diese Antwort geschrieben hast. Diese Verwirrung ist wichtig, da der Unterschied zwischen einem Generatorobjekt und einer Generatorfunktion der Unterschied zwischen dem Erhalten des gewünschten Verhaltens und dem Nachschlagen von Generatoren ist.
Blujay
15

Jeder hat eine wirklich nette und ausführliche Antwort mit Beispielen und ich schätze es sehr. Ich wollte nur ein paar kurze Antworten für Leute geben, die konzeptionell noch nicht ganz klar sind:

Wenn Sie Ihren eigenen Iterator erstellen, ist dies ein wenig kompliziert - Sie müssen eine Klasse erstellen und mindestens den Iter und die nächsten Methoden implementieren. Aber was ist, wenn Sie diesen Aufwand nicht durchlaufen und schnell einen Iterator erstellen möchten? Glücklicherweise bietet Python eine Abkürzung zum Definieren eines Iterators. Alles, was Sie tun müssen, ist, eine Funktion mit mindestens 1 Aufruf zu definieren. Wenn Sie diese Funktion aufrufen, gibt sie " etwas " zurück, das sich wie ein Iterator verhält (Sie können die nächste Methode aufrufen und in einer for-Schleife verwenden). Das etwas hat in Python einen Namen namens Generator

Hoffe das klärt ein bisschen.

Heapify
quelle
10

In früheren Antworten wurde dieser Zusatz übersehen: Ein Generator verfügt über eine closeMethode, typische Iteratoren dagegen nicht. Die closeMethode löst StopIterationim Generator eine Ausnahme aus, die in a abgefangen werden kannfinally Klausel in diesem Iterator enthalten ist, um die Möglichkeit zu erhalten, eine Bereinigung auszuführen. Diese Abstraktion macht es am nützlichsten in großen als einfachen Iteratoren. Man kann einen Generator schließen, wie man eine Datei schließen könnte, ohne sich darum kümmern zu müssen, was darunter liegt.

Meine persönliche Antwort auf die erste Frage wäre jedoch: iteratable hat nur eine __iter__Methode, typische Iteratoren haben nur eine __next__Methode, Generatoren haben sowohl eine __iter__als auch eine __next__und eine zusätzlicheclose .

Für die zweite Frage wäre meine persönliche Antwort: In einer öffentlichen Schnittstelle tendiere ich dazu, Generatoren sehr zu bevorzugen, da sie widerstandsfähiger sind: die closeMethode eine größere Kompositionsfähigkeit mityield from . Vor Ort kann ich Iteratoren verwenden, aber nur, wenn es sich um eine flache und einfache Struktur handelt (Iteratoren lassen sich nicht leicht zusammensetzen) und wenn Grund zu der Annahme besteht, dass die Sequenz ziemlich kurz ist, insbesondere wenn sie vor dem Ende gestoppt werden kann. Ich neige dazu, Iteratoren als ein Grundelement niedriger Ebene zu betrachten, außer als Literale.

Generatoren sind für Kontrollflussfragen ein ebenso wichtiges Konzept wie Versprechen: Beide sind abstrakt und zusammensetzbar.

Hibou57
quelle
Könnten Sie ein Beispiel geben, um zu veranschaulichen, was Sie meinen, wenn Sie über Komposition sprechen? Können Sie auch erklären, was Sie vorhaben, wenn Sie über " typische Iteratoren" sprechen ?
bli
1
Eine andere Antwort ( stackoverflow.com/a/28353158/1878788 ) besagt, dass "ein Iterator ein Iterator ist". __iter__Wie kommt es, dass ein Iterator __next__nur eine Methode haben kann, da er eine Methode hat ? Wenn sie iterabel sein sollen, würde ich erwarten, dass sie notwendigerweise auch haben __iter__.
bli
1
@bli: AFAICS Diese Antwort bezieht sich hier auf den Standard PEP234 , daher ist sie korrekt, während sich die andere Antwort auf eine Implementierung bezieht, sodass sie fraglich ist. Der Standard erfordert nur ein __iter__on iterables, um einen Iterator zurückzugeben, für den nur eine nextMethode erforderlich ist ( __next__in Python3). Bitte verwechseln Sie Standards (für die Eingabe von Enten) nicht mit ihrer Implementierung (wie ein bestimmter Python-Interpreter sie implementiert hat). Dies ist ein bisschen wie die Verwechslung zwischen Generatorfunktionen (Definition) und Generatorobjekten (Implementierung). ;)
Tino
7

Generatorfunktion, Generatorobjekt, Generator:

Eine Generatorfunktion ist wie eine reguläre Funktion in Python, enthält jedoch eine oder mehrere yieldAnweisungen. Generatorfunktionen sind ein großartiges Werkzeug, um Iteratorobjekte so einfach wie möglich zu erstellen . Das durch die Generatorfunktion zurückgegebene Iteratorobjekt wird auch als Generatorobjekt oder Generator bezeichnet .

In diesem Beispiel habe ich eine Generatorfunktion erstellt, die ein Generatorobjekt zurückgibt <generator object fib at 0x01342480>. Genau wie andere Iteratoren können Generatorobjekte in einer forSchleife oder mit der integrierten Funktion verwendet werden, next()die den nächsten Wert vom Generator zurückgibt.

def fib(max):
    a, b = 0, 1
    for i in range(max):
        yield a
        a, b = b, a + b
print(fib(10))             #<generator object fib at 0x01342480>

for i in fib(10):
    print(i)               # 0 1 1 2 3 5 8 13 21 34


print(next(myfib))         #0
print(next(myfib))         #1
print(next(myfib))         #1
print(next(myfib))         #2

Eine Generatorfunktion ist daher der einfachste Weg, ein Iterator-Objekt zu erstellen.

Iterator :

Jedes Generatorobjekt ist ein Iterator, aber nicht umgekehrt. Ein benutzerdefiniertes Iteratorobjekt kann erstellt werden, wenn seine Klasse eine Methode __iter__und eine __next__Methode implementiert (auch als Iteratorprotokoll bezeichnet).

Es ist jedoch viel einfacher, die Generatorfunktion zum Erstellen von Iteratoren zu verwenden, da diese ihre Erstellung vereinfachen. Ein benutzerdefinierter Iterator bietet Ihnen jedoch mehr Freiheit und Sie können auch andere Methoden gemäß Ihren Anforderungen implementieren, wie im folgenden Beispiel gezeigt.

class Fib:
    def __init__(self,max):
        self.current=0
        self.next=1
        self.max=max
        self.count=0

    def __iter__(self):
        return self

    def __next__(self):
        if self.count>self.max:
            raise StopIteration
        else:
            self.current,self.next=self.next,(self.current+self.next)
            self.count+=1
            return self.next-self.current

    def __str__(self):
        return "Generator object"

itobj=Fib(4)
print(itobj)               #Generator object

for i in Fib(4):  
    print(i)               #0 1 1 2

print(next(itobj))         #0
print(next(itobj))         #1
print(next(itobj))         #1
N Randhawa
quelle
6

Beispiele von Ned Batchelder werden für Iteratoren und Generatoren dringend empfohlen

Eine Methode ohne Generatoren, die gerade Zahlen macht

def evens(stream):
   them = []
   for n in stream:
      if n % 2 == 0:
         them.append(n)
   return them

während mit einem Generator

def evens(stream):
    for n in stream:
        if n % 2 == 0:
            yield n
  • Wir brauchen weder eine Liste noch eine returnErklärung
  • Effizient für Streams mit großer / unendlicher Länge ... es geht nur und liefert den Wert

Das Aufrufen der evensMethode (Generator) ist wie gewohnt

num = [...]
for n in evens(num):
   do_smth(n)
  • Generator auch verwendet, um Doppelschleife zu brechen

Iterator

Ein Buch voller Seiten ist iterierbar . Ein Lesezeichen ist ein Iterator

und dieses Lesezeichen hat nichts zu tun, außer sich zu bewegen next

litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration  (Exception) as we got end of the iterator

Um Generator zu benutzen ... brauchen wir eine Funktion

Um Iterator zu benutzen ... brauchen wir nextunditer

Wie gesagt:

Eine Generatorfunktion gibt ein Iteratorobjekt zurück

Der ganze Nutzen von Iterator:

Speichern Sie jeweils ein Element im Speicher

Marwan Mostafa
quelle
Über Ihr erstes Code-Snippet möchte ich wissen, was arg 'stream' sonst noch sein könnte als die Liste []?
Iqra.
5

Sie können beide Ansätze für dieselben Daten vergleichen:

def myGeneratorList(n):
    for i in range(n):
        yield i

def myIterableList(n):
    ll = n*[None]
    for i in range(n):
        ll[i] = i
    return ll

# Same values
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
for i1, i2 in zip(ll1, ll2):
    print("{} {}".format(i1, i2))

# Generator can only be read once
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)

print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))

# Generator can be read several times if converted into iterable
ll1 = list(myGeneratorList(10))
ll2 = myIterableList(10)

print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))

Wenn Sie den Speicherbedarf überprüfen, benötigt der Generator außerdem viel weniger Speicher, da nicht alle Werte gleichzeitig im Speicher gespeichert werden müssen.

Taschuhka
quelle
1

Ich schreibe auf sehr einfache Weise speziell für Python-Neulinge, obwohl Python im Grunde so viele Dinge tut.

Beginnen wir mit dem Grundlegenden:

Betrachten Sie eine Liste,

l = [1,2,3]

Schreiben wir eine äquivalente Funktion:

def f():
    return [1,2,3]

o / p von print(l): [1,2,3]& o / p vonprint(f()) : [1,2,3]

Lassen Sie uns die Liste iterierbar machen: In Python ist die Liste immer iterierbar, dh Sie können den Iterator jederzeit anwenden.

Wenden wir den Iterator auf die Liste an:

iter_l = iter(l) # iterator applied explicitly

Lassen Sie uns eine Funktion iterierbar machen, dh eine äquivalente Generatorfunktion schreiben. In Python, sobald Sie das Schlüsselwort eingeben yield; Es wird eine Generatorfunktion und der Iterator wird implizit angewendet.

Hinweis: Jeder Generator ist immer iterierbar, wenn der implizite Iterator angewendet wird. Hier ist der implizite Iterator der springende Punkt. Die Generatorfunktion lautet also:

def f():
  yield 1 
  yield 2
  yield 3

iter_f = f() # which is iter(f) as iterator is already applied implicitly

Wenn Sie also beobachtet haben, sobald Sie die Funktion eines Generators erstellt haben, ist dies bereits iter (f).

Jetzt,

l ist die Liste, nach Anwendung der Iteratormethode "iter" wird es iter (l)

f ist bereits iter (f), nach Anwendung der Iteratormethode "iter" wird es iter (iter (f)), was wiederum iter (f) ist.

Es ist irgendwie so, dass Sie int in int (x) umwandeln, das bereits int ist und int (x) bleibt.

Zum Beispiel o / p von:

print(type(iter(iter(l))))

ist

<class 'list_iterator'>

Vergiss niemals, dass dies Python ist und nicht C oder C ++

Daher lautet die Schlussfolgerung aus der obigen Erklärung:

Liste l ~ = iter (l)

Generatorfunktion f == iter (f)

Jyo der Hauch
quelle