2013-05-27 7 views
29

In Python 2 gab es einen Fehler, wenn Rückgabe zusammen mit Ausbeute in Funktionsdefinition war. Aber für diesen Code in Python 3.3Rückgabe in Generator zusammen mit der Ausbeute in Python 3.3

def f(): 
    return 3 
    yield 2 

x = f() 
print(x.__next__()) 

gibt es keinen Fehler, dass Rückkehr in Funktion mit der Ausbeute verwendet wird. Wenn jedoch die Funktion __next__ aufgerufen wird, wird die Ausnahme StopIteration ausgelöst. Warum gibt es nicht nur einen Rückgabewert 3? Wird diese Rückkehr irgendwie ignoriert?

+0

Interessant ... wenn ich versuche, 'def f():' wie in Python 3.2.5, bekomme ich: 'SyntaxError: 'return' mit argument in generator'. (Ich weiß, 3.2.x ist schon veraltet ...) – torek

+0

Ich habe Python 3.3.1 verwendet – scdmb

Antwort

36

Dies ist eine neue Funktion in Python 3.3 (wie ein Kommentar bemerkt, funktioniert es nicht einmal in 3.2). Ähnlich wie return in einem Generator war lange gleich raise StopIteration(), return <something> in einem Generator ist jetzt gleich raise StopIteration(<something>). Aus diesem Grund sollte die Ausnahme, die Sie sehen, als StopIteration: 3 gedruckt werden, und der Wert ist über das Attribut value für das Ausnahmeobjekt zugänglich. Wenn der Generator an die Verwendung der (auch neuen) yield from Syntax delegiert wird, ist es das Ergebnis. Details finden Sie unter PEP 380.

def f(): 
    return 1 
    yield 2 

def g(): 
    x = yield from f() 
    print(x) 

# g is still a generator so we need to iterate to run it: 
for _ in g(): 
    pass 

Dieser druckt 1, aber nicht 2.

+1

Außer es nicht (mit Python 3.4.3). – mcepl

+0

@mcepl Danke, jetzt behoben. In meinem Beispiel wurden die Generatoren nicht ausgeführt. – delnan

+0

@mcepl Welcher Teil "nicht" in 3.4.3? Ich nehme an, Sie beziehen sich darauf, wo es heißt "Wenn der Generator angewiesen ist, die (auch neue) Ausbeute von der Syntax zu verwenden, ist es das Ergebnis." Ich habe es gerade in 3.4.3 probiert und dort gibt yield den "returned" StopException Wert nicht weiter, was ich extrem merkwürdig und überraschend finde. Aber jetzt, wo ich 5 s darüber nachgedacht habe, macht es Sinn, weil der äußere Generator weiterläuft, nachdem der innere Generator fertig ist, und wenn der äußere Generator fertig ist, gibt er seine eigene StopIteration ab; während der vom inneren Generator emittierte in den Äther schwebt. – allyourcode

18

Der Rückgabewert wird nicht ignoriert, aber Generatoren geben nur Werte, ein return beendet nur den Generator, in diesem Fall früh. Das Vorrücken des Generators erreicht in diesem Fall niemals die yield-Anweisung.

Immer wenn ein Iterator das 'Ende' der zu liefernden Werte erreicht, muss ein StopIteration ausgelöst werden. Generatoren sind keine Ausnahme. Ab Python jedoch 3.3 wird jeder return Ausdruck den Wert der Ausnahme:

>>> def gen(): 
...  return 3 
...  yield 2 
... 
>>> try: 
...  next(gen()) 
... except StopIteration as ex: 
...  e = ex 
... 
>>> e 
StopIteration(3,) 
>>> e.value 
3 

Verwenden Sie die next() Funktion Iteratoren voranzutreiben, statt .__next__() direkt aufgerufen:

print(next(x)) 
+1

'return' mit einem Wert wird nicht ignoriert, es ist entweder ein Syntaxfehler (in 3.2 und früher) oder nicht ignoriert (in 3.3 und später). – delnan

+0

@delnan: Der Wert wird nicht verwendet; Sie können nicht 'return' anstelle von' yield' verwenden. Die Funktion endet, so dass "StopIteration" ausgelöst wird und der Rückgabewert bestenfalls verworfen wird. Das OP ** weiß ** vor 3.3 ist eine Rückgabe mit Wert ein Syntaxfehler. –

+1

'return' ist zwar kein Ersatz für' yield', aber der Wert ist * not * vergeblich. Es ist als Attribut des Ausnahmeobjekts verfügbar, und 'yield from' bietet einen bequemen Zugriff darauf. – delnan