Warum lösen Iteratoren in Python eine Ausnahme aus?

48

Hier ist die Syntax für Iteratoren in Java (etwas ähnliche Syntax in C #):

Iterator it = sequence.iterator();

while (it.hasNext()) {
    System.out.println(it.next());
}

Welches macht Sinn. Hier ist die äquivalente Syntax in Python:

it = iter(sequence)
while True:
    try:
        value = it.next() 
    except StopIteration:
        break
    print(value)

Ich dachte, Ausnahmen sollten nur unter außergewöhnlichen Umständen verwendet werden.

Warum verwendet Python Ausnahmen, um die Iteration zu stoppen?

NullUserException
quelle
2
Siehe auch
c_maker

Antworten:

50

Es gibt eine sehr pythonische Möglichkeit, diesen Ausdruck zu schreiben, ohne explizit einen try-except-Block für a zu schreiben StopIteration:

# some_iterable is some collection that can be iterated over
# e.g., a list, sequence, dict, set, itertools.combination(...)

for value in some_iterable:
    print(value)

Sie können sich über die relevanten PEPs 234 255 informieren, wenn Sie mehr darüber wissen möchten, warum sie StopIterationeingeführt wurden und welche Logik hinter Iteratoren steckt.

Ein allgemeines Prinzip in Python besteht darin, eine Möglichkeit zu haben, etwas zu tun (siehe import this), und vorzugsweise eine schöne, explizite, lesbare und einfache, die die pythonische Methode erfüllt. Ihr entsprechender Code ist nur erforderlich, da Python Iteratoren keine hasNextMember-Funktion gibt. Es ist vorzuziehen, die Iteratoren direkt durchzugehen (und wenn Sie etwas anderes tun müssen, um es einfach zu lesen und eine Ausnahme zu erkennen).

Dieses automatische Abfangen einer StopIterationAusnahme am Ende eines Iterators ist sinnvoll und entspricht dem EOFErrorAuslösen, wenn Sie nach einem Dateiende lesen.

Dr. Jimbob
quelle
6
Die "Pythonic" -Methode scheint sicher eher "nach Wert in Folge" zu sein als "nach Wert in Iter (Folge):". Beitrag aktualisieren?
Yam Marcovic
15
@ Yam: Ich stimme zu. Es ist nicht pythonisch, eine vorhandene Sequenz zu nehmen und in einen Iterator umzuwandeln, nur um eine for-Schleife darauf anzuwenden. Die Sequenz ist bereits iterabel, daher ist die Umwandlung von a listin a listiteratorsinnlos. Ich hielt die erste Zeile nur NullUserException der Ausgangspunkt zu folgen, zu erklären , wie Sie sollten Schleife über einen Iterator, der die gleiche Art und Weise ist , sollten Sie eine Schleife über jede iterable ( list, set, str, tuple, dict, file, generator, etc.). Ich hätte etwas tun können it = itertools.combinations("ABCDE", 2), um ein besseres Beispiel für einen aussagekräftigen Iterator zu erhalten.
Dr. Simbob
1
it = iter(sequence)wird nicht gebraucht.
Caridorc
2
@Caridorc - Wenn Sie die Kommentare gelesen haben, wurde Ihr Punkt angesprochen. Es ist nicht erforderlich und wurde durchgeführt, um dem Ausgangspunkt der Frage (wo sie explizit gefragt haben iterators) zu folgen, und Sie müssen iterexplizit ein iterator(try type([])( list) vs type(iter([]))( listiterator)) generieren .
Dr. Jimbob
//, @drjimbob, Sie haben im zweiten Kommentar zu dieser Frage einen hervorragenden Punkt angesprochen. Ich bin ein bisschen neu in den erweiterten Funktionen von iterables, und ich hätte das nicht mitbekommen, wenn ich die Kommentare nicht gelesen hätte. Ich denke, es würde vielen von uns armen, autodidaktischen Seelen zugute kommen, wenn wir den Punkt, wie die Frage selbst in "The Python Way" geändert werden könnte, als ersten Hauptteil der Antwort sehen.
Nathan Basanese
28

Der Grund, warum Python eine Ausnahme verwendet, um eine Iteration zu stoppen, ist in PEP 234 dokumentiert :

Es wurde in Frage gestellt, ob eine Ausnahme zum Signalisieren des Endes der Iteration nicht zu teuer ist. Es wurden mehrere Alternativen für die StopIteration-Ausnahme vorgeschlagen: ein spezieller Wert End, um das Ende zu signalisieren, eine Funktion end (), um zu testen, ob der Iterator beendet ist, und sogar die IndexError-Ausnahme erneut zu verwenden.

  • Ein spezieller Wert hat das Problem, dass, wenn eine Sequenz jemals diesen speziellen Wert enthält, eine Schleife über diese Sequenz ohne Warnung vorzeitig beendet wird. Wenn die Erfahrung mit nullterminierten C-Strings uns nicht die Probleme gelehrt hat, die dies verursachen kann, stellen Sie sich vor, wie schwierig es wäre, wenn ein Python-Introspection-Tool eine Liste aller integrierten Namen durchlaufen würde, vorausgesetzt, der spezielle End-Wert ist ein integrierter Wert. im Namen!

  • Das Aufrufen einer end () - Funktion würde zwei Aufrufe pro Iteration erfordern. Zwei Anrufe sind viel teurer als ein Anruf plus ein Test für eine Ausnahme. Insbesondere die zeitkritische Schleife kann ausnahmsweise sehr günstig getestet werden.

  • Das Wiederverwenden von IndexError kann zu Verwirrung führen, da es sich um einen echten Fehler handeln kann, der durch vorzeitiges Beenden der Schleife maskiert wird.

Hinweis: Die idiomatische Python-Methode zum Durchlaufen einer Sequenz sieht folgendermaßen aus:

for value in sequence:
    print (value)
Lesmana
quelle
20

Es ist ein Unterschied in der Philosophie. Die Pythonic-Designphilosophie lautet EAFP :

Bitten Sie lieber um Vergebung als um Erlaubnis. Dieser übliche Python-Codierungsstil setzt die Existenz gültiger Schlüssel oder Attribute voraus und fängt Ausnahmen ab, wenn sich die Annahme als falsch herausstellt. Dieser klare und schnelle Stil zeichnet sich durch das Vorhandensein von vielen tryund exceptAussagen aus. Die Technik steht im Gegensatz zum LBYL- Stil, den viele andere Sprachen wie C ...

Charles E. Grant
quelle
7

Es ist nur so, dass die Java-Implementierung eine hasNext()Methode hat, mit der Sie nach einem leeren Iterator suchen können, bevor Sie eine ausführen next(). Wenn Sie next()einen Java-Iterator aufrufen, der keine Elemente mehr enthält, NoSuchElementExceptionwird a geworfen .

So effektiv können Sie in Java einen try..catch ausführen wie in try..except in Python. Und ja, nach einer früheren Antwort ist Philosophie in der pythonischen Welt sehr wichtig.

Yati Sagade
quelle