2013-01-19 3 views
35

In diesem Codeabschnitt führt die Verwendung von 'for' zu keinem 'StopIteration' oder ist die For-Schleife für alle Ausnahmen zuständig und wird dann automatisch beendet? In diesem Fall, warum haben wir die äußere "Rückkehr" ?? Oder ist die raise StopIteration verursacht durch: return None? return None:Warum wird als nächstes eine 'StopIteration' ausgelöst, aber 'für' eine normale Rückkehr?

#!/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) 

Unter der Annahme, StopIteration wird durch ausgelöst. Wann wird GeneratorExit generiert?

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 ein tun:

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

In diesem Fall, warum ich kein Zurückverfolgungs sehen?

Antwort

44

Die for Schleife überwacht explizit StopIteration.

Der Zweck der for-Anweisung besteht darin, die von einem Iterator bereitgestellte Sequenz zu durchlaufen, und die Ausnahme wird verwendet, um zu signalisieren, dass der Iterator jetzt fertig ist; for fängt keine anderen Ausnahmen ab, die von dem Objekt, über das iteriert wird, ausgelöst werden.

Das ist, weil StopIteration das normale, erwartete Signal ist, zu sagen, wer iteriert, dass es nichts mehr zu produzieren gibt.

Eine Generatorfunktion ist eine spezielle Art von Iterator; es erhöht in der Tat StopIteration, wenn die Funktion erledigt ist (d. h. wenn es zurückkehrt, also ja, return None erhöht StopIteration). Es ist eine Anforderung von Iteratoren; sie müssen erhöhen StopIteration, wenn sie fertig sind; in der Tat, wenn ein StopIteration angehoben worden ist, versucht, ein anderes Element von ihnen zu erhalten (durch next() oder den Aufruf den .next() (iV 2) oder .__next__() (iV 3) Methode auf dem Iterator) muss immer StopIteration wieder erhöhen.

GeneratorExit ist eine Ausnahme für die Kommunikation in der anderen Richtung. Sie sind explizit Schließen ein Generator mit einem yield Ausdruck, und die Art und Weise, wie Python diese Schließung an den Generator kommuniziert, ist durch Erhöhung GeneratorExit innerhalb dieser Funktion. Sie fangen diese Ausnahme innerhalb von countdown explizit ein. Ihr Zweck besteht darin, dass ein Generator Ressourcen beim Schließen wie benötigt bereinigt.

Ein GeneratorExit wird nicht an den Aufrufer weitergegeben; siehe die generator.close() documentation.

+5

Beachten Sie auch, für nur die Ausnahme beim Auswerten des Iterators zu fangen. Es fängt es nicht ein, wenn es innerhalb des Körpers der for-Schleife angehoben wird. –

+1

@JonasWielicki: erweiterte den Satz etwas, um diese Interpretation zu entfernen. :-) –