Wir können eine Liste einfach und direkt durch Listengenerierung erstellen, aber aufgrund von Speicherbeschränkungen ist die Listenkapazität definitiv begrenzt. Darüber hinaus nimmt die Erstellung einer Liste mit 1 Million Elementen nicht nur viel Speicherplatz in Anspruch, sondern wenn wir nur auf die ersten paar Elemente zugreifen müssen, wird der von den meisten nachfolgenden Elementen belegte Platz verschwendet.
Wenn also die Listenelemente nach einem bestimmten Algorithmus berechnet werden können, können wir dann während der Schleife kontinuierlich nachfolgende Elemente berechnen? Dadurch entfällt die Notwendigkeit, eine vollständige Liste zu erstellen, was viel Platz spart. In Python wird dieser Mechanismus zum gleichzeitigen Schleifen und Berechnen als Generator bezeichnet.
Um einen Generator zu erstellen, gibt es viele Möglichkeiten. Die erste Methode ist sehr einfach. Ändern Sie einfach das [] eines Listengenerierungsausdrucks in (), um einen Generator zu erstellen:
>>> mylist = [ x for x in range(1, 10)] >>> mylist [1, 2, 3, 4, 5, 6, 7, 8, 9] >>> gen = (x for x in range(1,10)) >>> gen <generator object <genexpr> at 0x7f1d7fd0f5a0>
Der einzige Unterschied zwischen dem Erstellen von mylist und gen ist das äußerste [] und (). , mylist ist eine Liste und gen ist ein Generator.
Wir können jedes Element der Liste direkt ausdrucken, aber wie drucken wir jedes Element des Generators aus?
Wenn Sie sie einzeln ausdrucken möchten, können Sie die next()-Methode des Generators verwenden:
>>> gen.next() 1 >>> gen.next() 2 >>> gen.next() 3 ... >>> gen.next() 9 >>> gen.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
Wie gesagt, der Generator speichert den Algorithmus und jeden Wenn next() aufgerufen wird, berechnet es den Wert des nächsten Elements, bis das letzte Element berechnet ist. Wenn keine weiteren Elemente vorhanden sind, wird ein StopIteration-Fehler ausgegeben.
Tatsächlich können wir die for-Schleife anstelle der next()-Methode verwenden, was eher den Ideen einer effizienten Programmierung entspricht:
>>> gen = ( x for x in range(1, 10)) >>> for num in gen: ... print num ... 1 2 3 4 5 6 7 8 9
Generator ist sehr leistungsfähig. Wenn der Berechnungsalgorithmus relativ komplex ist und nicht über eine for-Schleife ähnlich der Listengenerierung implementiert werden kann, kann er auch über eine Funktion implementiert werden.
Zum Beispiel kann in der berühmten Fibonacci-Folge mit Ausnahme der ersten und zweiten Zahl jede beliebige Zahl durch Addition der ersten beiden Zahlen erhalten werden:
1 , 1, 2, 3, 5 , 8, 13, 21, 34, ...
Die Fibonacci-Folge kann nicht mit der Listengenerierung geschrieben werden, aber es ist einfach, sie mit einer Funktion auszudrucken:
def fib(max): n = 0 a, b = 0, 1 while n < max: print b a, b = b, a + b n = n + 1
Die Die obige Funktion kann die ersten N Zahlen der Fibonacci-Folge ausgeben:
>>> fib(6)
Wenn Sie genau hinschauen, können Sie sehen, dass die Fib-Funktion tatsächlich Fibonacci definiert. Die Berechnungsregeln der Borachi-Folge können vom ersten Element ausgehen und berechnen alle nachfolgenden Elemente. Diese Logik ist tatsächlich dem Generator sehr ähnlich.
Mit anderen Worten, die obige Funktion ist nur einen Schritt vom Generator entfernt. Um die fib-Funktion in einen Generator umzuwandeln, ändern Sie einfach print b in yield b:
def fib(max): n = 0 a, b = 0, 1 while n < max: yield b a, b = b, a + b n = n + 1
Dies ist eine weitere Möglichkeit, einen Generator zu definieren. Wenn eine Funktionsdefinition das Schlüsselwort yield enthält, ist die Funktion keine gewöhnliche Funktion mehr, sondern ein Generator:
>>> fib(6)
Hier ist es am schwierigsten zu verstehen, dass der Ausführungsablauf von Generator und Funktion gleich ist anders. . Funktionen werden nacheinander ausgeführt und kehren zurück, wenn sie auf eine Return-Anweisung oder die letzte Zeile von Funktionsanweisungen stoßen. Die Funktion, die zum Generator wird, wird jedes Mal ausgeführt, wenn next() aufgerufen wird, kehrt zurück, wenn eine Yield-Anweisung auftritt, und setzt die Ausführung ab der Yield-Anweisung fort, die beim letzten Mal zurückgegeben wurde, wenn sie erneut ausgeführt wird.
Als einfaches Beispiel definieren Sie einen Generator, der die Zahlen 1, 3 und 5 nacheinander zurückgibt:
>>> def odd(): ... print 'step 1' ... yield 1 ... print 'step 2' ... yield 3 ... print 'step 3' ... yield 5 ... >>> o = odd() >>> o.next() step 1 1 >>> o.next() step 2 3 >>> o.next() step 3 5 >>> o.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
Wie Sie sehen können, ist odd keine gewöhnliche Funktion, sondern eine Wenn der Generator während des Ausführungsprozesses auf Yield stößt, wird er unterbrochen und die Ausführung wird beim nächsten Mal fortgesetzt. Nachdem yield dreimal ausgeführt wurde, ist kein yield mehr auszuführen, daher wird beim vierten Aufruf von next() ein Fehler gemeldet.
Zurück zum Fib-Beispiel: Wenn wir während der Schleife weiterhin yield aufrufen, wird sie weiterhin unterbrochen. Natürlich müssen Sie eine Bedingung festlegen, damit die Schleife die Schleife verlässt, andernfalls wird eine unendliche Zahl aufgelistet.
In ähnlicher Weise verwenden wir nach dem Ändern der Funktion in Generator grundsätzlich nie next() zum Aufrufen, sondern verwenden direkt die for-Schleife zum Iterieren:
>>> for n in fib(6): ... print n ...
Generator ist ein sehr leistungsfähiges Werkzeug In Python können Sie den Listengenerierungsausdruck einfach in einen Generator umwandeln oder komplexe Logikgeneratoren über Funktionen implementieren.
Um das Funktionsprinzip des Generators zu verstehen, berechnet er während der for-Schleife kontinuierlich das nächste Element und beendet die for-Schleife unter geeigneten Bedingungen. Wenn bei einem von einer Funktion geänderten Generator die Return-Anweisung auftritt oder die letzte Zeile des Funktionskörpers ausgeführt wird, ist dies die Anweisung zum Beenden des Generators, und die for-Schleife endet entsprechend.