Heim  >  Artikel  >  Backend-Entwicklung  >  Tiefes Verständnis von Generatoren in Python

Tiefes Verständnis von Generatoren in Python

黄舟
黄舟Original
2016-12-16 11:36:541685Durchsuche

Generatorkonzept

Der Generator speichert die Ergebnisse nicht in einer Reihe, sondern speichert den Status des Generators und gibt bei jeder Iteration einen Wert zurück, bis er auf eine StopIteration Finish-Ausnahme stößt.

Generatorsyntax

Generatorausdruck: Dieselbe Listenanalysesyntax, aber [] in der Listenanalyse durch () ersetzen
Was Generatorausdrücke bewirken können, ist eine Listenanalyse. Grundsätzlich kann es verarbeitet werden, Wenn die zu verarbeitende Sequenz jedoch relativ groß ist, ist die Listenanalyse speicherintensiver.

>>> gen = (x**2 für x in range(5))
>>> gen
;
>>> für g in gen:
... print(g, end='-')
...
0-1-4-9-16-
>>> für x in [0,1,2,3,4,5]:
... print(x, end='-')
...
0-1-2-3-4-5-

Generatorfunktion: Wenn das Schlüsselwort yield in der Funktion vorkommt, dann ist die Funktion keine gewöhnliche Funktion mehr, sondern eine Generatorfunktion.
Aber die Generatorfunktion kann eine unbegrenzte Reihenfolge erzeugen, sodass die Liste überhaupt nicht verarbeitet werden kann.
Die Funktion von yield besteht darin, eine Funktion in einen Generator umzuwandeln. Eine Funktion mit yield ist keine gewöhnliche Funktion mehr und der Python-Interpreter behandelt sie als Generator.

Das Folgende ist eine Generatorfunktion, die unendlich ungerade Zahlen erzeugen kann.

def odd():
n=1
while True:
yield n
n+=2
odd_num = odd()
count = 0
for o in odd_num:
if count >=5: break
print(o)
count +=1

Ähnliche Dinge können natürlich auch manuell erreicht werden Iteratoren schreiben Der Effekt ist, dass der Generator intuitiver und leichter zu verstehen ist

class Iter:
def __init__(self):
self.start=-1
def __iter__(self) :
return self
def __next__(self):
self.start +=2
return self.start
I = Iter()
for count in range(5):
print( next(I))

Exkurs: Der Generator enthält die Methoden __iter() und next__(), sodass Sie for direkt zum Iterieren verwenden können, es gibt jedoch keinen selbstgeschriebenen Iter Dazu gehört StopIteration. Die Iteration kann nur über eine manuelle Schleife durchgeführt werden.

>>> aus Sammlungen importieren Iterable
>>> >>>> >>>> ; iter(odd_num) ist odd_num
True>>> help(odd_num)
Hilfe zum Generatorobjekt:

odd = Klassengenerator(objekt)
| Hier definiert:
|
|. __iter__(self, /)
|. Implementieren Sie iter(self).
|
|. Implementieren Sie next(self) .
...


Sehen Sie sich die obigen Ergebnisse an. Jetzt können Sie sicher eine Schleife gemäß der Iterator-Methode durchführen!

Wenn die for-Schleife ausgeführt wird, führt jede Schleife den Code innerhalb der Fab-Funktion aus. Wenn sie Yield b erreicht, gibt die Fab-Funktion einen Iterationswert zurück. In der nächsten Iteration beginnt der Code mit der nächsten Anweisung von yield b. Die Ausführung wird fortgesetzt und die lokalen Variablen der Funktion sehen genauso aus wie vor der letzten Unterbrechung, sodass die Funktion weiter ausgeführt wird, bis sie erneut auf yield trifft. Es sieht so aus, als ob eine Funktion während der normalen Ausführung mehrmals durch yield unterbrochen wird und jede Unterbrechung den aktuellen Iterationswert über yield zurückgibt.

Ausbeute und Rückgabe

Wenn es in einem Generator keine Rückgabe gibt, wird StopIteration standardmäßig zurückgegeben, wenn die Funktion abgeschlossen ist.

>>> g1( ):

... yield 1

...

>>> g=g1()

>>> g) bleibt nach der Ausführung der Yield-Anweisung hängen, sodass die Programmausführung zu diesem Zeitpunkt noch nicht beendet ist.

1

>>> next(g) #Das Programm versucht, die Ausführung ab der nächsten Anweisung der yield-Anweisung zu starten und stellt fest, dass sie das Ende erreicht hat, sodass eine StopIteration-Ausnahme ausgelöst wird.

Traceback (letzter Aufruf zuletzt):
Datei „“, Zeile 1, in
StopIteration
>>>


Wenn während der Ausführung eine Rückgabe auftritt, wird StopIteration direkt ausgelöst, um die Iteration zu beenden.

>>> >>> g=g2()
>>> #Das Programm bleibt an der Position, nachdem die yield 'a'-Anweisung ausgeführt wurde.
'a'
>>> next(g) #Das Programm stellt fest, dass die nächste Anweisung return ist, daher wird eine StopIteration-Ausnahme ausgelöst, sodass die yield 'b'-Anweisung niemals ausgeführt wird.
Traceback (letzter Aufruf zuletzt):
Datei „“, Zeile 1, in
StopIteration


Wenn nach der Rückgabe ein Wert zurückgegeben wird , dann ist dieser Wert die Beschreibung der StopIteration-Ausnahme, nicht der Rückgabewert des Programms.

Es gibt keine Möglichkeit für einen Generator, Return zu verwenden, um einen Wert zurückzugeben.

>>> > g=g3()

>>> next(g)

'hello'

>>> next(g)

Traceback (letzter Anruf zuletzt):

Datei „“, Zeile 1, in
StopIteration: world


Vom Generator unterstützte Methoden

>>> ; help(odd_num)
Hilfe zum Generatorobjekt:

odd = Klassengenerator(objekt)
|. Hier definierte Methoden:
......

|. ..)

|. close() -> raise GeneratorExit.

|. send(...)

|.
|. Nächsten ausgegebenen Wert zurückgeben oder StopIteration auslösen.
|. throw(...)
|. throw(typ[,val[,tb]]) ->
|. Nächsten ausgegebenen Wert zurückgeben oder StopIteration auslösen.
......


close()

Generatorfunktion manuell schließen, nachfolgende Aufrufe werden durchgeführt Gibt direkt die StopIteration-Ausnahme zurück.

>>>... Ausbeute 1
... Ausbeute 2
... Ausbeute 3
...
>>> g=g4()
>>> next(g)

1

>>> g.close()

>>> ; next(g) #Nach dem Schließen funktionieren die Anweisungen yield 2 und yield 3 nicht mehr

Traceback (letzter Aufruf zuletzt):

Datei „“, Zeile 1, in

StopIteration


send()

Das größte Merkmal der Generatorfunktion besteht darin, dass sie eine externe Variable akzeptieren und diese zurückgeben kann, nachdem das Ergebnis basierend auf dem Variableninhalt berechnet wurde.
Dies ist der am schwierigsten zu verstehende Teil der Generatorfunktion und auch der wichtigste Teil. Die Implementierung der Coroutine, über die ich später sprechen werde, hängt davon ab.

def gen():
value=0
while True:
receive=yield value
if keep=='e':
value = 'got: % s' % empfangen

g=gen()

print(g.send(None))

print(g.send('aaa'))
print( g.send( 3))

print(g.send('e'))



Ausführungsprozess:

über g.send(None) oder next(g ) kann der gestartet werden Generatorfunktion und führen Sie sie bis zum Ende der ersten yield-Anweisung aus.
Zu diesem Zeitpunkt wurde die Yield-Anweisung ausgeführt, dem Empfang wurde jedoch kein Wert zugewiesen.
Der Ertragswert gibt den Anfangswert 0 aus.
Hinweis: Sie können beim Starten der Generatorfunktion nur (Keine) senden. Wenn Sie versuchen, andere Werte einzugeben, erhalten Sie eine Fehlermeldung.


Über g.send('aaa') wird aaa übergeben und dem Empfang zugewiesen. Anschließend wird der Wert berechnet und an den while-Kopf zurückgegeben Die Aussage wird beendet.
Zu diesem Zeitpunkt gibt der Ertragswert „got: aaa“ aus und bleibt dann hängen.

Durch g.send(3) wird Schritt 2 wiederholt und das endgültige Ausgabeergebnis ist „got: 3“

Wenn wir g.send(' e'), führt das Programm break aus und verlässt dann die Schleife. Schließlich wird die gesamte Funktion ausgeführt, sodass eine StopIteration-Ausnahme ausgelöst wird.
Das endgültige Ausführungsergebnis lautet wie folgt:

0
erhalten: aaa

erhalten: 3

Traceback (letzter Aufruf zuletzt):
Datei „h.py“ , Zeile 14, in
print(g.send('e'))

StopIteration


throw()


wird zum Senden von Entering verwendet Eine Ausnahme kann eine systemdefinierte Ausnahme oder eine benutzerdefinierte Ausnahme beenden.

Nach throw() wird direkt eine Ausnahme ausgelöst und das Programm beendet, oder es wird ein Ertrag verbraucht, oder es wird direkt mit dem Ende des Programms fortgefahren, wenn es keinen nächsten Ertrag gibt.

def gen():
while True:
try:
yield 'normal value'
yield 'normal value 2'
print('here')
außer ValueError ; .throw(ValueError))
print(next(g))
print(g.throw(TypeError))


Das Ausgabeergebnis ist:

normal Wert
wir haben hier ValueError
Normalwert
Normalwert 2

Traceback (letzter Aufruf zuletzt):

Datei „h.py“, Zeile 15, in

print (g.throw(TypeError))

StopIteration


Erklärung:

print(next(g)): Der Normalwert wird ausgegeben und bleibt bei der Ausbeute „normal“. Wert 2 'Vorher.


Da g.throw(ValueError) ausgeführt wird, werden alle nachfolgenden Try-Anweisungen übersprungen, was bedeutet, dass yield „Normalwert 2“ nicht ausgeführt wird. Geben Sie dann die Ausnahmeanweisung ein und geben Sie We aus Habe hier ValueError.

Dann geben Sie den while-Anweisungsteil erneut ein und verbrauchen dabei einen Yield, sodass der Normalwert ausgegeben wird.

print(next(g)) führt die Yield-Anweisung „Normalwert 2“ aus und bleibt an der Position nach der Ausführung der Anweisung.

g.throw(TypeError): Springt aus der Try-Anweisung, sodass print('here') nicht ausgeführt wird. Führen Sie dann die Break-Anweisung aus, springen Sie aus der While-Schleife und Erreichen Sie dann das Ende des Programms und führen Sie die StopIteration-Ausnahme aus.

Das Folgende ist ein umfassendes Beispiel zum Erweitern einer mehrdimensionalen Liste oder zum Reduzieren einer mehrdimensionalen Liste)

def flatten(nested):

try:

    #Wenn es sich um eine Zeichenfolge handelt, lösen Sie manuell TypeError aus.

if isinstance(nested, str):

raise TypeError

for sublist in nested:

for element in flatten(sublist):
            #yield element
Print ('Got:' , Element)
Except Typeerror:
#Print ('Here')
Yield Nested

L = ['AAADF' ,3],2,4,[5,[6, [8,[9]],'ddf'],7]]
für num in flatten(L):
print(num)


Wenn es etwas schwierig ist Wenn Sie verstehen, ist es klarer, den Kommentar der Druckanweisung zu öffnen und anzuzeigen.

Yield from

Die von Yield generierte Funktion ist ein Iterator, daher fügen wir sie normalerweise in eine Schleifenanweisung ein, um das Ergebnis auszugeben.
Manchmal müssen wir den durch diese Ausbeute generierten Iterator in eine andere Generatorfunktion einfügen, nämlich in die Generatorverschachtelung.
Zum Beispiel:

def inner():

for i in range(10):

yield i

def äußere():

g_inner=inner( ) # Dies ist ein Generator
while True:
res = g_inner.send(None)

yield res


g_outer=outer()
while True:
try :
print(g_outer.send(None))
außer StopIteration:
break


Zu diesem Zeitpunkt können wir die yield from-Anweisung verwenden, um unsere Arbeitsbelastung zu reduzieren.

def äußere2():
yield from inner()


Natürlich liegt der Schwerpunkt der yield from-Anweisung darauf, uns dabei zu helfen, automatisch Ausnahmen zwischen dem inneren und dem äußeren zu behandeln Hier sind zwei gut geschriebene Artikel, daher werde ich nicht zu ausführlich sein.

http://blog.theerrorlog.com/yield-from-in-python-3.html

http://stackoverflow.com/questions/9708902/in-practice-what-are-the-main -uses-for-the-new-yield-from-syntax-in-python-3

Zusammenfassung

Nach der Entenmodelltheorie ist der Generator eine Art Iterator, der das kann mit for iterate durchgeführt werden.


Wenn next(generator) zum ersten Mal ausgeführt wird, wird das Programm nach der Ausführung der Yield-Anweisung angehalten und alle Parameter und Status werden gespeichert.
Wenn next(generator) erneut ausgeführt wird, wird es aus dem angehaltenen Zustand ausgeführt.

Die Schleife endet, wenn das Ende des Programms erreicht wird oder StopIteration auftritt.

Sie können Parameter über generator.send(arg) übergeben, das das Coroutine-Modell ist.

Sie können eine Ausnahme über generator.throw(Exception) übergeben. Die throw-Anweisung verbraucht einen Yield.
Der Generator kann manuell über generator.close() geschlossen werden.

next() entspricht send(None)


Das Obige ist das detaillierte Verständnis von Generatoren in Python. Weitere verwandte Artikel finden Sie im PHP-Chinesisch Website (www.php.cn)!


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Vorheriger Artikel:Python und CodierungNächster Artikel:Python und Codierung