2012-09-28 1 views
16

Non riesco a capire il metodo send. Capisco che è usato per far funzionare il generatore. Ma la sintassi è qui: generator.send(value).Python 3: metodo di invio dei generatori

In qualche modo non riesco a capire perché il valore dovrebbe diventare il risultato dell'attuale espressione yield. Ho preparato un esempio:

def gen(): 
    for i in range(10): 
     X = yield i 
     if X == 'stop': 
      break 
     print("Inside the function " + str(X)) 

m = gen() 
print("1 Outside the function " + str(next(m)) + '\n') 
print("2 Outside the function " + str(next(m)) + '\n') 
print("3 Outside the function " + str(next(m)) + '\n') 
print("4 Outside the function " + str(next(m)) + '\n') 
print('\n') 
print("Outside the function " + str(m.send(None)) + '\n') # Start generator 
print("Outside the function " + str(m.send(77)) + '\n') 
print("Outside the function " + str(m.send(88)) + '\n') 
#print("Outside the function " + str(m.send('stop')) + '\n') 
print("Outside the function " + str(m.send(99)) + '\n') 
print("Outside the function " + str(m.send(None)) + '\n') 

Il risultato è:

1 Outside the function 0 

Inside the function None 
2 Outside the function 1 

Inside the function None 
3 Outside the function 2 

Inside the function None 
4 Outside the function 3 



Inside the function None 
Outside the function 4 

Inside the function 77 
Outside the function 5 

Inside the function 88 
Outside the function 6 

Inside the function 99 
Outside the function 7 

Inside the function None 
Outside the function 8 

Beh, francamente, mi è sorprendente.

  1. Nella documentazione si legge che quando viene eseguita una dichiarazione yield, lo stato del generatore è congelato e il valore di expression_list viene restituito al chiamante next s’. Beh, non sembra che sia successo. Perché è possibile eseguire l'istruzione if e la funzione print all'interno di gen().
  2. Come posso capire perché lo standard X all'interno e all'esterno della funzione è diverso? Ok. Supponiamo che send(77) trasmetta 77 a m. Bene, l'espressione yield diventa 77. Allora cos'è X = yield i? E come la funzione 77 si converte in 5 quando si verifica all'esterno?
  3. Perché la prima stringa del risultato non riflette nulla che sta succedendo all'interno del generatore?

In ogni caso, potresti commentare in qualche modo queste dichiarazioni send e yield?

risposta

40

Quando si utilizza send e l'espressione yield in un generatore, lo si considera come una coroutine; un thread di esecuzione separato che può essere eseguito in sequenza intercalata ma non in parallelo con il relativo chiamante.

Quando il chiamante esegue R = m.send(a), inserisce l'oggetto a nello slot di ingresso del generatore, trasferisce il controllo al generatore e attende una risposta. Il generatore riceve l'oggetto a come risultato di X = yield i e viene eseguito finché non raggiunge un'altra espressione yield ad es. Y = yield j. Quindi inserisce j nello slot di uscita, trasferisce il controllo al chiamante e attende che riprenda nuovamente. Il chiamante riceve j come risultato di R = m.send(a) e viene eseguito finché non raggiunge un'altra istruzione S = m.send(b) e così via.

R = next(m) è lo stesso di R = m.send(None); sta inserendo None nello slot di input del generatore, quindi se il generatore verifica il risultato di X = yield i allora X sarà None.

Come metafora, si consideri un dumb waiter:

Dumb waiter

Quando il server riceve un ordine da un cliente, hanno messo il pad nel cameriere muto, send alla cucina, e attendere dal sportello per il piatto:

R = kitchen.send("Ham omelette, side salad") 

lo chef (che è stato in attesa da parte del portellone) raccoglie l'ordine, prepara il piatto, yield s al ristorante, e attende per il prossimo ordine:

next_order = yield [HamOmelette(), SideSalad()] 

Il server (che sta aspettando dal portellone) prende il piatto al cliente e ritorna con un altro ordine, ecc

Poiché sia ​​il server e lo chef di attesa da parte del schiudono dopo send ing un ordine oppure yield in un piatto, c'è una sola persona che fa qualcosa alla volta, cioè il processo è a thread singolo. Entrambe le parti possono utilizzare il normale flusso di controllo, in quanto i macchinari del generatore (il muto cameriere) si occupano dell'esecuzione di interleaving.

+0

semplicemente non viene eseguito contemporaneamente in parallelo. – jfs

+0

@ J.F.Sebastian Penso che "simultaneamente" significhi in parallelo? – ecatmur

+2

in programmazione "parallelo" implica "concurrent", ma il contrario in generale non è vero. [Differenza tra programmazione simultanea e programmazione parallela] (http://stackoverflow.com/q/1897993/4279) – jfs

4
def gen(): 
    i = 1 
    while True: 
     i += 1 
     x = yield i 
     print(x) 

m = gen() 
next(m) 
next(m) 
m.send(4) 

risultato

None 
4 

un'occhiata a codici più semplificati di cui sopra.
Penso che la cosa che ha portato alla tua confusione sia 'x = yield i' statment, questo statment non dice che il metodo accettato da send() è stato assegnato a io quindi ho assegnato a x. Invece, il valore i viene restituito dalla dichiarazione di rendimento al generatore, x è assunta dal metodo send(). Una dichiarazione fa due cose contemporaneamente.

9

La parte più confusa dovrebbe essere questa linea X = yield i, specialmente quando si chiama send() sul generatore. In realtà l'unica cosa che dovete sapere è:

a livello lessicale: next() è uguale a send(None)

nel livello di interprete: X = yield i equivale al di sotto di linee (ORDINE MATERIA) :

yield i 
# won't continue until next() or send() is called 
# and this is also the entry point of next() or send() 
X = the_input_of_send 

e, le 2 righe di commento è la ragione esatta, perché abbiamo bisogno di ca ll send(None) per la prima volta, poiché il generatore ritorna i (resa i) prima assegnare il valore di X

+0

Ottima risposta! Spiegazione semplice e facile Hai identificato molto bene il punto debole 'X = yield i'. verrebbe superato di 10 volte –