Warum wird als nächstes eine 'StopIteration' ausgelöst, aber 'for' eine normale Rendite?

72

Warum führt die Verwendung forin diesem Codeteil zu no StopIteration oder forfängt die Schleife alle Ausnahmen ein und wird dann stillschweigend beendet? In welchem ​​Fall haben wir das Fremde return? Oder ist das raise StopIterationverursacht durch : return None?

#!/usr/bin/python3.1
def countdown(n):
    print("counting down")
    while n >= 9:
        yield n
        n -= 1
    return

for x in countdown(10):
    print(x)

c = countdown(10)
next(c)
next(c)
next(c)

Angenommen, StopIterationwird ausgelöst durch : return None. Wann wird GeneratorExitgeneriert?

def countdown(n):
    print("Counting down from %d" % n)
    try:
        while n > 0:
            yield n
            n = n - 1
    except GeneratorExit:
        print("Only made it to %d" % n)

Wenn ich manuell Folgendes mache:

c = countdown(10)
c.close() #generates GeneratorExit??

In welchem ​​Fall sehe ich keinen Traceback?

Sophros
quelle

Antworten:

96

Die forSchleife lauscht StopIterationexplizit.

Der Zweck der forAnweisung besteht darin, die von einem Iterator bereitgestellte Sequenz zu durchlaufen, und die Ausnahme wird verwendet, um zu signalisieren, dass der Iterator jetzt fertig ist. forfängt keine anderen Ausnahmen ab, die durch das Objekt ausgelöst werden, über das iteriert wird, nur diese.

Das StopIterationliegt daran, dass das normale, erwartete Signal jedem, der iteriert, mitteilt, dass nichts mehr produziert werden muss.

Eine Generatorfunktion ist eine spezielle Art von Iterator. es wird tatsächlich ausgelöst, StopIterationwenn die Funktion abgeschlossen ist (dh wenn es zurückkehrt, also ja, wird return Noneausgelöst StopIteration). Es ist eine Anforderung von Iteratoren; sie müssen erhöhen, StopIterationwenn sie fertig sind; in der Tat, einmal StopIterationangehoben worden ist , versucht , ein anderes Element von ihnen zu erhalten (durch next()oder den Aufruf .next()(py 2) oder .__next__()(iV 3) Methode auf dem Iterator) muss immer heben StopIterationwieder.

GeneratorExitist eine Ausnahme für die Kommunikation in die andere Richtung. Sie schließen einen Generator explizit mit einem yieldAusdruck, und Python kommuniziert diesen Abschluss mit dem Generator, indem er GeneratorExitinnerhalb dieser Funktion erhöht . Sie fangen diese Ausnahme explizit innerhalb von ab countdown. Sie dient dazu, dass ein Generator beim Schließen die Ressourcen nach Bedarf bereinigt.

A GeneratorExitwird nicht an den Anrufer weitergegeben; siehe die generator.close()Dokumentation .

Martijn Pieters
quelle
5
Beachten Sie auch, dass for die Ausnahme nur beim Auswerten des Iterators abfängt. Es wird es nicht fangen, wenn es im Körper der for-Schleife angehoben wird.
Jonas Schäfer
1
@ JonasWielicki: erweiterte den Satz ein wenig, um diese Interpretation zu entfernen. :-)
Martijn Pieters