Wie funktioniert dieser Code, der die Zuweisung und den Ertragsoperator umfasst? Die Ergebnisse sind ziemlich verwirrend.

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)

Ausgabe:

[1, None, 2, None, 3, None] 
[1, 2, 3]
26
Charlie Haley 21 Aug. 2015 im 00:07

3 Antworten

Beste Antwort

Mit der Zuweisungssyntax ("Yield-Ausdruck") können Sie den Generator als rudimentäre Coroutine behandeln.

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

Der Client-Code, der mit dem Generator arbeitet, kann mithilfe seiner send() -Methode Daten zurück in den Generator übertragen. Auf diese Daten kann über die Zuweisungssyntax zugegriffen werden.

send() wird ebenfalls iteriert - es enthält also tatsächlich einen next() -Aufruf.

Anhand Ihres Beispiels würde die Couroutine-Funktionalität folgendermaßen verwendet:

>>> 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 Sendungen aufgrund der zweiten yield in jeder Schleifeniteration verloren gehen, die die gesendeten Daten nicht erfasst.

BEARBEITEN: Ich habe vergessen, die None in Ihrem Beispiel zu erklären.

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

Wenn eine Generatorfunktion mit einer next () -Methode fortgesetzt wird, wird der aktuelle Ertragsausdruck immer als Keine ausgewertet.

next() wird bei Verwendung der Iterationssyntax verwendet.

16
Jeremy Brown 20 Aug. 2015 im 22:14

Um die Antwort von TigerhawkT3 zu erweitern, liegt der Grund dafür, dass die Ertragsoperation None in Ihrem Code zurückgibt, darin, dass list(r1) nichts in den Generator sendet. Versuche dies:

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!'))

Ausgabe:

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

Hier ist ein etwas hergestelltes Beispiel, bei dem das Senden von Werten an einen Generator nützlich sein kann:

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()

Ausgabe:

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
4
Cyphase 20 Aug. 2015 im 21:32
_ = yield i 
yield _

Zuerst ist es yield der Wert, auf den i verweist, z. 1. Dann ergibt es den Wert, der von der Operation yield zurückgegeben wird, nämlich None. Dies geschieht bei jeder Iteration der Schleife.

for i in x:
    _ = yield i

Dies ist einfach yield der Wert, auf den i verweist, z. 1, fährt dann mit der nächsten Iteration der Schleife fort und erzeugt 2, dann 3.

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

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

Wenn der Interpreter nun ein yield sieht, generiert er den angegebenen Wert. In diesem Fall gibt diese Operation jedoch den Wert None zurück, genau wie mylist.append(0) oder print('hello') return den Wert None. Wenn Sie dieses Ergebnis einer Referenz wie _ zuweisen, speichern Sie diese None.

Im ersten Snippet geben Sie also ein Objekt aus, speichern dann das "Ergebnis" dieser yield Operation, die None ist, und dann yield das {{X3 }}. Im zweiten Snippet geben Sie ein Objekt aus und speichern dann das "Ergebnis" dieser yield Operation, aber Sie nie yield dieses Ergebnis , also None erscheint nicht in der Ausgabe.

Beachten Sie, dass yield nicht immer None zurückgibt - genau das haben Sie mit send() an den Generator gesendet. Da dies in diesem Fall nichts war, erhalten Sie None. Weitere Informationen zu {{{. X4}}.

6
Community 23 Mai 2017 im 10:29