2014-06-22 20 views
5

ja studiuje odpowiedzi z Nosklo w What is the most "pythonic" way to iterate over a list in chunks?, gdzie określonej funkcji:pętli wewnętrznej instrukcji return

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

Czy ktoś tłumaczy mi, jak działa, gdy zwrot następuje pętli for? Próbowałem wykonać następujące czynności:

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

, ale nie uzyskuję tego samego wyniku. Uwaga niż w przykładzie Nosklo, chunker() nazywa iteracyjnie jak w poniższym przykładzie:

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

Dodając wydruki, zauważyłem, że ten ostatni for pętla jest wykonywana 3 razy (to idzie trhough listy animals), ale dla pętli w chunker funkcja jest wykonywana tylko raz. Jak to jest, że jest tylko jeden powrót i mogę zobaczyć 3 odciski?

+0

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

+3

To nie jest pętla for; to wyrażenie generujące. Wygląda to trochę jak pętla for, która sprawia, że ​​początkujący ciągle myślą, że to jeden. Zobacz http://stackoverflow.com/questions/1756096/understanding-generators-in-python i http://legacy.python.org/dev/peps/pep-0289/ – user2357112

+0

Dziękuję bardzo! Sprawdzę te linki. – user3764177

Odpowiedz

3

Wartość zwracana przez funkcję nosklo chunker nazywana jest generatorem, obiektem, który generuje wartości podczas iteracji. W tym przypadku generator jest tworzony przy użyciu generator expression, niepodzielnego fragmentu kodu między nawiasami: (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] 

chodzi o to, co jest wywoływana, możemy przepisać kod pętli tak, żeby go zobaczyć lepsze:

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

The chunker funkcji jest wywoływana tylko raz i zwraca obiekt generatora, który możemy zapisać w generator zmienna. Pętla for działa tylko z tym obiektem generator i wywołuje go 3 razy.

Aby móc drukować faktyczne połączenia z tym generatorem, należy dodać wyrażenie print do wyrażenia generatora (seq[pos:pos + size] for pos in xrange(0, len(seq), size)), co jest niedozwolone. Ale możemy przepisać to wyrażenie generator do normalnego generator function pomocą instrukcji yield, bardziej rozwlekły, ale także bardziej wszechstronną formę generatora, gdzie możemy zawierać oświadczenie print i która będzie działać zgodnie z oczekiwaniami początkowo:

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

Tutaj sama funkcja chunker2 jest faktycznym generatorem i zostaje wywołana 3 razy.

+0

Dzięki. Oznacza to, że w przykładzie Nosklo "następny" odpowiada różnym iteracjom w pętli dla grupy w klocku (zwierzęta, 3): – user3764177

+0

@ user3764177 Prawidłowo, funkcja 'next' pobiera kolejny element z obiektu iterowalnego, tylko tak samo jak jedna iteracja pętli 'for'. – famousgarkin

Powiązane problemy