2011-10-25 6 views
26

So come funziona yield. Conosco la permutazione, la penso come una semplice matematica.Dove usare meglio la resa in Python?

Ma qual è la vera forza di yield? Quando dovrei usarlo? Un esempio semplice e buono è meglio.

+0

possibile duplicato di [La parola chiave yield resa Python spiegata] (http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained) – JBernardo

risposta

53

yield si utilizza al meglio quando si ha una funzione che restituisce una sequenza e si desidera eseguire un'iterazione su quella sequenza, ma non è necessario avere ogni valore in memoria in una volta.

Ad esempio, ho uno script python che analizza un ampio elenco di file CSV e voglio restituire ogni riga da elaborare in un'altra funzione. Non voglio archiviare tutti i megabyte di dati in una volta, quindi ho yield ogni riga in una struttura dati python. Così la funzione per ottenere righe dal file potrebbe essere simile:

def get_lines(files): 
    for f in files: 
     for line in f: 
      #preprocess line 
      yield line 

posso quindi utilizzare la stessa sintassi con le liste per l'accesso l'uscita di questa funzione:

for line in get_lines(files): 
    #process line 

ma salvare un molto uso della memoria.

+0

Da dove viene il "yield" in questo esempio? – poplitea

+0

grazie, sono un po 'confuso. per la riga in f.readlines(): la riga #process fa lo stesso. sembra che non ci sia bisogno di cedere, o di cedere in readall()? – whi

+0

Ho aggiunto la definizione della funzione effettiva per chiarire questo – murgatroid99

3

Un altro utilizzo è in un client di rete. Usa 'yield' in una funzione generatore per round robin attraverso più socket senza la complessità dei thread.

Ad esempio, avevo un client di test hardware che aveva bisogno di inviare un piano R, G, B di un'immagine al firmware. I dati dovevano essere spediti a passo: rosso, verde, blu, rosso, verde, blu. Invece di generare tre thread, avevo un generatore che leggeva dal file, codificava il buffer. Ogni buffer era un "rendimento". Fine del file, funzione restituita e ho avuto end-iteration.

Il mio codice client passava attraverso le tre funzioni del generatore, ottenendo i buffer fino alla fine dell'iterazione.

+0

grazie. sì '3 thread + lock' non va bene. ma perché nello stesso thread principale? – whi

+0

Semplicità. Lo script era una piccola app a linea di comando. Nessuna GUI. Inoltre, tutto nello stesso thread significava che un errore su un socket avrebbe arrestato l'intero client. Dato che stavo parlando con un solo server, un decesso di una presa significava che avrei potuto interrompere rapidamente tutte le prese. –

15

In poche parole, yield fornisce un generatore. Lo useresti dove normalmente useresti uno return in una funzione. Come esempio molto artificiosa tagliato e incollato da un prompt ...

>>> def get_odd_numbers(i): 
...  return range(1, i, 2) 
... 
>>> def yield_odd_numbers(i): 
...  for x in range(1, i, 2): 
...    yield x 
... 
>>> foo = get_odd_numbers(10) 
>>> bar = yield_odd_numbers(10) 
>>> foo 
[1, 3, 5, 7, 9] 
>>> bar 
<generator object yield_odd_numbers at 0x1029c6f50> 
>>> bar.next() 
1 
>>> bar.next() 
3 
>>> bar.next() 
5 

Come si può vedere, nel primo caso foo detiene l'intero elenco in memoria in una sola volta. Non è un grosso problema per una lista con 5 elementi, ma cosa succede se vuoi una lista di 5 milioni? Non solo è un enorme divoratore di memoria, ma richiede anche molto tempo per essere costruito nel momento in cui viene chiamata la funzione. Nel secondo caso, bar ti dà solo un generatore. Un generatore è un iterabile, il che significa che è possibile utilizzarlo in un ciclo for, ecc., Ma è possibile accedere a ciascun valore solo una volta. Tutti i valori non vengono anche memorizzati in memoria nello stesso momento; l'oggetto generatore "ricorda" dove si trovava nel loop l'ultima volta che l'hai chiamato - in questo modo, se usi un iterabile per (dire) conta fino a 50 miliardi, non devi contare fino a 50 miliardi tutti subito e memorizzare i 50 miliardi di numeri per contare. Ancora una volta, questo è un esempio abbastanza forzato, probabilmente useresti lo itertools se davvero volessi contare fino a 50 miliardi. :)

Questo è il caso di utilizzo più semplice dei generatori. Come hai detto, può essere usato per scrivere permutazioni efficienti, usando yield per spingere le cose attraverso lo stack di chiamate invece di usare una sorta di variabile stack. I generatori possono essere utilizzati anche per attraversamenti di alberi specializzati e ogni sorta di altre cose.

Ulteriori approfondimenti:

+2

Il secondo esempio contiene anche l'intero elenco in memoria in una volta, poiché è necessario mantenere l'intero elenco per il retro del generatore. – user2357112

1

sto leggendo algoritmi e strutture dati in Python

C'è una funzione fabonacci usando rendimento. Penso che sia il momento migliore per usare la resa.

def fibonacci(): 
    a, b = 0, 1 
    while True: 
     yield a 
     a, b = b, a+b 

è possibile utilizzare questo tipo:

f = fibonacci() 
for i, f in enumerate(f): 
    print i, f 
    if i >= 100: break 

Quindi, penso, forse, quando l'elemento successivo dipende elementi precedenti, è il momento di usare la resa.