2014-06-22 9 views
5

stavo studiando la risposta da Nosklo in What is the most "pythonic" way to iterate over a list in chunks?, dove ha definito la funzione:ciclo for all'interno di un'istruzione return

def chunker(seq, size): 
    return (seq[pos:pos + size] for pos in xrange(0, len(seq), size)) 

Qualcuno mi spiega come funziona il ritorno quando è seguito da un ciclo for? Ho cercato di fare quanto segue:

def chunker2(seq, size): 
    for pos in xrange(0, len(seq), size): 
    return seq[pos:pos + size] 

ma non ottengo lo stesso risultato. Nota che in Nosklo esempio, chunker() è chiamato in modo iterativo, come nell'esempio qui sotto:

animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish'] 
for group in chunker(animals, 3): 
    print group 

Con l'aggiunta di stampe, ho notato che quest'ultimo for ciclo viene eseguito 3 volte (si va trhough lista animals), ma il per loop nella funzione chunker viene eseguita una sola volta. Quindi, come mai c'è solo un ritorno e posso vedere 3 stampe?

+0

https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions – Bakuriu

+3

Questo non è un ciclo for; è un'espressione generatrice. Assomiglia molto a un ciclo for, il che rende i principianti costantemente convinti che sia uno. Vedi http://stackoverflow.com/questions/1756096/understanding-generators-in-python e http://legacy.python.org/dev/peps/pep-0289/ – user2357112

+0

Grazie mille! Controllerò quei link. – user3764177

risposta

3

Il valore restituito della funzione nosklo chunker è chiamato generatore, un oggetto che genererà valori quando viene iterato. In questo caso il generatore viene creato utilizzando generator expression, una parte di codice indivisibile tra parentesi: (seq[pos:pos + size] for pos in xrange(0, len(seq), size)).

>>> def chunker(seq, size): 
...  return (seq[pos:pos + size] for pos in xrange(0, len(seq), size)) 
... 
>>> result = chunker([1, 2, 3], 2) 
>>> print(result) 
<generator object <genexpr> at 0x10581e1e0> 
>>> next(result) 
[1, 2] 
>>> next(result) 
[3] 

Per quanto riguarda quello che viene chiamato, siamo in grado di riscrivere il codice di ciclo come questo per vedere meglio:

>>> generator = chunker(animals, 3) 
>>> for chunk in generator: 
...  print chunk 
... 
['cat', 'dog', 'rabbit'] 
['duck', 'bird', 'cow'] 
['gnu', 'fish']  

Le chunker funzioni viene chiamato solo una volta e restituisce l'oggetto generatore, che memorizziamo nel generator variabile. Il ciclo for funziona quindi solo con questo oggetto generator e lo chiama per 3 volte.

Per poter stampare le chiamate effettive di questo generatore, è necessario includere l'istruzione print all'interno dell'espressione generatore (seq[pos:pos + size] for pos in xrange(0, len(seq), size)), che non è consentita. Ma possiamo riscrivere questa espressione generatore ad una normale generator function utilizzando l'istruzione yield, una forma più prolisso, ma anche più versatile del generatore in cui possiamo includere la dichiarazione print e che funzionerà come previsto inizialmente:

>>> def chunker2(seq, size): 
...  for pos in xrange(0, len(seq), size): 
...   print('chunker2 generator called') 
...   yield seq[pos:pos + size] 
... 
>>> for group in chunker2(animals, 3): 
...  print group 
... 
chunker2 generator called 
['cat', 'dog', 'rabbit'] 
chunker2 generator called 
['duck', 'bird', 'cow'] 
chunker2 generator called 
['gnu', 'fish'] 

Qui la stessa funzione chunker2 è il generatore effettivo e viene chiamata 3 volte.

+0

Grazie. Questo significa che nell'esempio Nosklo, il "prossimo" corrisponde alle diverse iterazioni nel ciclo per il gruppo in chunker (animali, 3): – user3764177

+0

@ user3764177 Corretto, la funzione 'next' ottiene l'oggetto successivo dall'oggetto iterabile solo il come un'iterazione del ciclo 'for'. – famousgarkin