2015-08-20 14 views
10

Come funziona questo codice, che implica l'assegnazione e l'operatore di rendimento? I risultati sono piuttosto confusi.Python: assegnazione di resa e rendimento

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) 

uscita:

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

I Non sono esattamente sicuro del perché, ma l'unica domanda È probabile che appaia nell'elenco "Correlato" in qualsiasi domanda Python sia [la domanda 'yield'] (http://stackoverflow.com/questions/231767/what-does-the-yield-keyword-do-in-python), anche se la domanda in questione non ha nulla a che fare con 'yield'. Questa volta, tuttavia, è effettivamente correlato. – TigerhawkT3

risposta

12

La sintassi di assegnazione ("espressione di resa") consente di trattare il generatore come una coroutine rudimentale.

Prima proposto in PEP 342 e documentato qui: https://docs.python.org/2/reference/expressions.html#yield-expressions

Il codice client che sta lavorando con il generatore può comunicare dati indietro nel generatore utilizzando il metodo send(). Questi dati sono accessibili tramite la sintassi di assegnazione.

send() eseguirà anche l'iterazione, quindi include effettivamente una chiamata next().

Usando il tuo esempio, questo è ciò che sarebbe come utilizzare la funzionalità di couroutine:

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

Nota che alcune delle manda si perdono a causa della seconda yield in ogni iterazione del ciclo che non rifletta i dati inviati

MODIFICA: Hai dimenticato di spiegare i valori None s ottenuti nel tuo esempio.

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

Quando una funzione generatore viene ripreso con un metodo next(), l'attuale espressione resa restituisce sempre Nessuno.

next() viene utilizzato quando si utilizza la sintassi di iterazione.

2
_ = yield i 
yield _ 

Innanzitutto yield s il valore fa riferimento i, ad esempio 1. Quindi restituisce il valore restituito dall'operazione yield, che è None. Lo fa su ogni iterazione del ciclo.

for i in x: 
    _ = yield i 

Questo semplicemente yield s il valore fa riferimento i, ad esempio 1, quindi procede alla successiva iterazione del ciclo, producendo 2, quindi 3.

differenza return, la parola chiave yield può essere utilizzato in un'espressione:

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

Ora, quando l'interprete vede un yield, genererà il valore indicato. Tuttavia, quando lo fa, tale operazione restituisce il valore None, proprio come mylist.append(0) o print('hello') sarà return il valore None. Quando assegni quel risultato a un riferimento come _, stai salvando quello None.

Così, nel primo frammento, si sta producendo un oggetto, quindi si salva il "risultato" di quella yield operazione, che è None, e poi si yield che None. Nel secondo frammento, si produce un oggetto, quindi si salva il "risultato" di tale operazione yield, ma si mai yield che risulta, quindi None non viene visualizzato nell'output.

Si noti che yield non restituirà sempre None - questo è solo ciò che è stato inviato al generatore con send(). Dal momento che non era niente in questo caso, ottieni None. Vedi this answer per ulteriori informazioni su send().

+0

@JohnKugelman - Certo, aggiungerò qualche spiegazione più dettagliata. – TigerhawkT3

+0

@JohnKugelman - Informazioni aggiunte. Per favore fatemi sapere se una qualsiasi delle parole non è chiara o fuorviante. – TigerhawkT3

3

Per espandere sulla risposta di TigerhawkT3, la ragione per cui l'operazione di rendimento sta tornando None nel codice è perché list(r1) non invia nulla in generatore. Prova questo:

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

uscita:

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

Ecco un esempio un po 'fabbricato in cui l'invio di valori in un generatore potrebbe essere utile:

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

uscita:

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 
Problemi correlati