806 Stimmen

Unterschied zwischen Pythons Generatoren und Iteratoren

Was ist der Unterschied zwischen Iteratoren und Generatoren? Einige Beispiele für die Verwendung der beiden Fälle wären hilfreich.

8voto

N Randhawa Punkte 7493

Generatorfunktion, Generatorobjekt, Generator:

A Generator-Funktion ist genau wie eine reguläre Funktion in Python, aber sie enthält eine oder mehrere yield Erklärungen. Generatorfunktionen sind ein großartiges Werkzeug zum Erstellen von Iterator Objekte so einfach wie möglich. Die Iterator Objekt, das von der Generatorfunktion zurückgegeben wird, heißt auch Generator-Objekt o Stromerzeuger .

In diesem Beispiel habe ich eine Generator-Funktion erstellt, die ein Generator-Objekt zurückgibt <generator object fib at 0x01342480> . Genau wie andere Iteratoren können Generator-Objekte in einer for Schleife oder mit der eingebauten Funktion next() der den nächsten Wert von 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 also der einfachste Weg, ein Iterator-Objekt zu erstellen.

Iterator :

Alle Generatorobjekt ist ein Iterator aber nicht umgekehrt. Ein benutzerdefiniertes Iterator-Objekt kann erstellt werden, wenn seine Klasse Folgendes implementiert __iter__ y __next__ Methode (auch Iteratorprotokoll genannt).

Es ist jedoch viel einfacher, die Generatorenfunktion zu verwenden, um Iteratoren weil sie ihre Erstellung vereinfachen, aber ein benutzerdefinierter Iterator gibt Ihnen mehr Freiheit und Sie können auch andere Methoden entsprechend 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

5voto

tashuhka Punkte 4768

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)))

Außerdem benötigt der Generator viel weniger Speicherplatz, da er nicht alle Werte gleichzeitig im Speicher ablegen muss, wenn man sich den Speicherbedarf ansieht.

5voto

Manngo Punkte 10358

Eine iterierbares Objekt ist etwas, das (natürlich) iteriert werden kann. Dazu brauchen Sie allerdings so etwas wie eine Iterator-Objekt und, ja, die Terminologie kann verwirrend sein. Iterable-Objekte enthalten eine __iter__ Methode, die das Iterator-Objekt für das Iterable-Objekt zurückgibt.

Ein Iterator-Objekt ist ein Objekt, das die Iterator-Protokoll - eine Reihe von Regeln. In diesem Fall muss es mindestens diese beiden Methoden haben: __iter__ y __next__ . Die __next__ Methode ist eine Funktion, die einen neuen Wert liefert. Die __iter__ Methode gibt das Iterator-Objekt zurück. In einem komplexeren Objekt kann ein separater Iterator vorhanden sein, aber in einem einfacheren Fall, __iter__ gibt das Objekt selbst zurück (typischerweise return self ).

Ein iterierbares Objekt ist ein list Gegenstand. Es ist kein Iterator, aber es hat eine __iter__ Methode, die einen Iterator zurückgibt. Sie können diese Methode direkt aufrufen als things.__iter__() , oder verwenden Sie iter(things) .

Wenn Sie eine beliebige Sammlung durchlaufen wollen, müssen Sie deren Iterator verwenden:

things_iterator = iter(things)
for i in things_iterator:
    print(i)

Python verwendet jedoch automatisch den Iterator, weshalb Sie das obige Beispiel nie sehen. Stattdessen schreiben Sie:

for i in things:
    print(i)

Einen Iterator selbst zu schreiben, kann mühsam sein, deshalb hat Python eine einfachere Alternative: die Generatorfunktion . Eine Generatorfunktion ist keine gewöhnliche Funktion. Anstatt den Code zu durchlaufen und ein Endergebnis zurückzugeben, wird der Code zurückgestellt, und die Funktion kehrt sofort mit einer Generatorobjekt .

Ein Generator-Objekt ist wie ein Iterator-Objekt, da es das Iterator-Protokoll implementiert. Das ist für die meisten Zwecke gut genug. Es gibt viele Beispiele für Generatoren in den anderen Antworten.

Kurz gesagt, ein Iterator ist ein Objekt, mit dem man durch ein anderes Objekt iterieren kann, sei es eine Sammlung oder eine andere Quelle von Werten. Ein Generator ist ein vereinfachter Iterator, der mehr oder weniger die gleiche Aufgabe erfüllt, aber einfacher zu implementieren ist.

Normalerweise würden Sie sich für einen Generator entscheiden, wenn das alles ist, was Sie brauchen. Wenn Sie jedoch ein komplexeres Objekt bauen, das unter anderem Iteration beinhaltet, würden Sie stattdessen das Iterator-Protokoll verwenden.

2voto

Jyo the Whiff Punkte 787

Ich schreibe speziell für Python-Neulinge in einer sehr einfachen Art und Weise, obwohl Python tief im Inneren so viele Dinge tut.

Beginnen wir mit den grundlegenden Dingen:

Betrachten Sie eine Liste,

l = [1,2,3]

Lassen Sie uns eine entsprechende Funktion schreiben:

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

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

Machen wir die Liste l iterierbar: In Python ist eine Liste immer iterierbar, d.h. Sie können Iteratoren anwenden, wann immer Sie wollen.

Wenden wir den Iterator auf die Liste an:

iter_l = iter(l) # iterator applied explicitly

Machen wir eine Funktion iterierbar, d.h. schreiben wir eine entsprechende Generatorfunktion. In Python, sobald Sie das Schlüsselwort yield wird sie zu einer Generatorfunktion und der Iterator wird implizit angewendet.

Hinweis: Jeder Generator ist immer iterierbar, wenn ein impliziter Iterator angewendet wird, und hier ist der implizite Iterator der Knackpunkt Die Generatorfunktion wird also sein:

def f():
  yield 1 
  yield 2
  yield 3

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

Sie haben also festgestellt, dass die Funktion f, sobald Sie sie zu einem Generator gemacht haben, bereits iter(f) ist

Jetzt,

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

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

Es ist wie Sie sind Casting int zu int (x), die bereits int ist und es wird int (x) bleiben.

Zum Beispiel o/p von :

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

es

<class 'list_iterator'>

Vergessen Sie nie, dass dies Python und nicht C oder C++ ist.

Die Schlussfolgerung aus der obigen Erklärung lautet daher:

Liste l ~= iter(l)

Generatorfunktion f == iter(f)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X