2015-02-02 3 views
26

Ich lief heute einen Fehler, der kam, weil ich next() verwendet, um einen Wert zu extrahieren, und "nicht gefunden" gibt eine StopIteration aus.next() spielt nicht gut mit irgendwelchen/allen in Python

Normalerweise, die das Programm anhalten würde, aber die Funktion next, um innerhalb einer all() Iteration wurde genannt, so die all nur früh beendet und kehrte True.

Ist dies ein erwartetes Verhalten? Gibt es Styleguides, die solche Dinge vermeiden helfen?

Vereinfachtes Beispiel:

def error(): return next(i for i in range(3) if i==10) 
error() # fails with StopIteration 
all(error() for i in range(2)) # returns True 
+0

Das gleiche geschieht in Python 3. – khelwood

+0

@khelwood Dank, werde ich das py2.7 Tag – amwinter

+9

Natürlich entfernen. [** all ** * (iterable) * Gibt True zurück, wenn alle Elemente des Iterables wahr sind *** (oder wenn das Iterable leer ist) ***.] (https://docs.python.org/2/ library/functions.html # all) –

Antwort

24

Obwohl dies das Standardverhalten in Python-Versionen bis einschließlich 3.6 ist, wird es in der Sprache als Fehler angesehen und soll in Python 3.7 geändert werden, sodass stattdessen eine Ausnahme ausgelöst wird.

Wie PEP 479 sagt:

Die Wechselwirkung von Generatoren und StopIteration derzeit etwas überraschend ist, und obskuren Fehler verbergen kann. Eine unerwartete Ausnahme sollte nicht zu einem geringfügig veränderten Verhalten führen, sondern zu einem verrauschten und leicht zu debuggenden Traceback führen. Gegenwärtig wird StopIteration, das versehentlich innerhalb einer Generatorfunktion ausgelöst wird, als das Ende der Iteration durch das Schleifenkonstrukt interpretiert, das den Generator antreibt.

Ab Python 3.5 ist es möglich, das Standardverhalten auf das für 3.7 geplante zu ändern. Dieser Code:

# gs_exc.py 

from __future__ import generator_stop 

def error(): 
    return next(i for i in range(3) if i==10) 

all(error() for i in range(2)) 

... stellt sich die folgende Ausnahme:

Traceback (most recent call last): 
    File "gs_exc.py", line 8, in <genexpr> 
    all(error() for i in range(2)) 
    File "gs_exc.py", line 6, in error 
    return next(i for i in range(3) if i==10) 
StopIteration 

The above exception was the direct cause of the following exception: 

Traceback (most recent call last): 
    File "gs_exc.py", line 8, in <module> 
    all(error() for i in range(2)) 
RuntimeError: generator raised StopIteration 

In Python 3.5 und 3.6 ohne die __future__ Import, wird eine Warnung ausgelöst wird.Zum Beispiel:

# gs_warn.py 

def error(): 
    return next(i for i in range(3) if i==10) 

all(error() for i in range(2)) 

$ python3.5 -Wd gs_warn.py 
gs_warn.py:6: PendingDeprecationWarning: generator '<genexpr>' raised StopIteration 
    all(error() for i in range(2)) 

$ python3.6 -Wd gs_warn.py 
gs_warn.py:6: DeprecationWarning: generator '<genexpr>' raised StopIteration 
    all(error() for i in range(2)) 
+0

danke - Ich hatte die Daumen gedrückt von StopIteration überrascht sprudeln machte mich nicht verrückt – amwinter

+0

Bedeutet dies, dass 'alle (some_iterator)' und 'mlist = [i für i in some_iterator]; alle (mlist)' würde sich verhalten anders? – tdelaney

+0

@tdelaney Ja, wenn 'some_iterator' ein Generator ist, der' StopIteration' auslöst. –

9

Das Problem ist nicht all in verwenden, ist es, dass Sie einen Generator Ausdruck als Parameter zu all haben. Der StopIteration wird an den Generator-Ausdruck weitergegeben, der nicht wirklich weiß, woher er stammt, also macht er das Übliche und beendet die Iteration.

Sie können dies sehen Ihre error Funktion mit etwas durch den Ersatz, die den Fehler auslöst, direkt:

def error2(): raise StopIteration 

>>> all(error2() for i in range(2)) 
True 

Das letzte Stück des Puzzles ist, zu wissen, was all tut mit einer leeren Sequenz:

>>> all([]) 
True 

Wenn Sie direkt next verwenden möchten, sollten Sie bereit sein, sich selbst zu fangen StopIteration.

Bearbeiten: Schön zu sehen, dass die Python-Entwickler dies als einen Fehler betrachten und Schritte unternehmen, um es in 3.7 zu ändern.

+0

Warum wird der Fehler nicht mit 'all' propagiert? –

+1

ok - also sagst du, die Lösung ist, 'keine' /' alle 'um irgendetwas zu verwenden, das eine' StopIteration' auslösen kann? – amwinter

+0

@PadraicCunningham ist es durch den Generator Ausdruck selbst gefangen. –

Verwandte Themen