2015-08-20 17 views
10

Wie funktioniert dieser Code, der die Zuweisung und den Yield-Operator umfasst? Die Ergebnisse sind eher verwirrend.Python: yield- und Yield-Zuweisung

def test1(x): 
    for i in x: 
     _ = yield i 
     yield _ 
def test2(x): 
    for i in x: 
     _ = yield i 

r1 = test1([1,2,3]) 
r2 = test2([1,2,3]) 
print list(r1) 
print list(r2) 

Ausgang:

[1, None, 2, None, 3, None] 
[1, 2, 3] 
+0

I Bin mir nicht ganz sicher warum, aber die eine Frage mos t in der Python-Frage in der "Related" -Liste erscheint, ist die [yield-Frage] (http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python)), auch wenn die fragliche Frage nichts mit "Ertrag" zu tun hat. Diesmal ist es jedoch verwandt. – TigerhawkT3

Antwort

12

Mit der Zuweisungssyntax ("yield expression") können Sie den Generator als eine rudimentäre Coroutine behandeln.

Zuerst in PEP 342 vorgeschlagen und hier dokumentiert: https://docs.python.org/2/reference/expressions.html#yield-expressions

Der Client-Code, der mit dem Generator arbeitet können Daten kommunizieren zurück in den Generator unter Verwendung seiner send() Methode. Diese Daten sind über die Zuweisungssyntax zugänglich.

send() wird auch iterieren - so enthält es tatsächlich einen next() Aufruf.

Arbeiten mit dem Beispiel, das ist, was es sein würde, die couroutine Funktionalität zu verwenden:

>>> def test1(x): 
...  for i in x: 
...   _ = yield i 
...   yield _ 
... 
>>> l = [1,2,3] 
>>> gen_instance = test1(l) 

>>> #First send has to be a None 
>>> print gen_instance.send(None) 
1 
>>> print gen_instance.send("A") 
A 
>>> print gen_instance.send("B") 
2 
>>> print gen_instance.send("C") 
C 
>>> print gen_instance.send("D") 
3 
>>> print gen_instance.send("E") 
E 
>>> print gen_instance.send("F") 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
StopIteration 

Beachten Sie, dass einige der Sends wegen der zweiten yield in jeder Schleife Iteration verloren, die nicht erfassen ist die gesendeten Daten.

EDIT: Vergessen zu erklären, die None s ergab in Ihrem Beispiel.

Von https://docs.python.org/2/reference/expressions.html#generator.next:

Wenn eine Generatorfunktion mit einer nächsten() Methode wieder aufgenommen wird, wertet die aktuellen Ausbeute Ausdruck immer auf Keine.

next() wird verwendet, wenn die Iterationssyntax verwendet wird.

2
_ = yield i 
yield _ 

Zuerst es yield s die durch i referenzierten Wert, z.B. 1. Dann gibt es den Wert zurück, der von der yield-Operation zurückgegeben wird, die None ist. Dies geschieht bei jeder Iteration der Schleife.

for i in x: 
    _ = yield i 

Dieses einfach yield s die durch i referenzierten Wert, z.B. 1, geht dann zur nächsten Iteration der Schleife über und erzeugt 2, dann 3.

Im Gegensatz zu return, das yield Schlüsselwort kann in einem Ausdruck verwendet werden:

x = return 0 # SyntaxError 
x = yield 0 # perfectly fine 

Nun, wenn der Dolmetscher ein yield sieht, wird es den angegebenen Wert generieren. Wenn dies jedoch der Fall ist, gibt diese Operation den Wert None zurück, genau wie mylist.append(0) oder print('hello') wird return der Wert None. Wenn Sie dieses Ergebnis einer Referenz wie _ zuweisen, speichern Sie diese None.

also im ersten Schnipsel, sind Sie ein Objekt ergeben, dann speichern Sie das „Ergebnis“ der yield Operation, die None ist, und dann Sie yield dass None. Im zweiten Ausschnitt geben Sie ein Objekt, dann speichern Sie das "Ergebnis" dieser yield Operation, aber Sie nie yield das Ergebnis, so None erscheint nicht in der Ausgabe.

Beachten Sie, dass yield nicht immer None zurückgibt - dies ist nur, was Sie an den Generator mit send() gesendet haben. Da das in diesem Fall nichts war, bekommst du None. Siehe this answer für mehr auf send().

+0

@JohnKugelman - Klar, ich füge eine ausführlichere Erklärung hinzu. – TigerhawkT3

+0

@JohnKugelman - Informationen hinzugefügt. Bitte lassen Sie mich wissen, wenn eine der Formulierungen unklar oder irreführend ist. – TigerhawkT3

3

Um der Grund auf TigerhawkT3 Antwort, dass die Ausbeute Betrieb None im Code zurückkehrt ist zu erweitern, weil list(r1) nichts zu senden ist in der Generator. Versuchen Sie folgendes:

def test1(x): 
    for i in x: 
     _ = yield i 
     yield _ 


r1 = test1([1, 2, 3]) 

for x in r1: 
    print(' x', x) 
    print('send', r1.send('hello!')) 

Ausgang:

x 1 
send hello! 
    x 2 
send hello! 
    x 3 
send hello! 

Hier ist ein etwas hergestelltes Beispiel, bei dem Wert in einen Generator Senden nützlich sein könnte:

def changeable_count(start=0): 
    current = start 
    while True: 
     changed_current = yield current 
     if changed_current: 
      current = changed_current 
     else: 
      current += 1 

counter = changeable_count(10) 

for x in range(20): 
    print(next(counter), end=' ') 

print() 
print() 

print('Sending 51, printing return value:', counter.send(51)) 
print() 

for x in range(20): 
    print(next(counter), end=' ') 

print() 
print() 

print('Sending 42, NOT printing return value') 
print() 

counter.send(42) 

for x in range(20): 
    print(next(counter), end=' ') 

print() 

Ausgang:

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 

Sending 51, printing return value: 51 

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 

Sending 42, NOT printing return value 

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62