2010-02-09 10 views
80
s = [1,2,3,4,5,6,7,8,9] 
n = 3 

zip(*[iter(s)]*n) # returns [(1,2,3),(4,5,6),(7,8,9)] 

Jak działa zip(*[iter(s)]*n)? Jak by to wyglądało, gdyby było napisane bardziej szczegółowym kodem?Jak działa zip (* [iter (s)] * n) w Pythonie?

+1

również zajrzeć tutaj, gdzie to działa, jest również wyjaśnione: http://stackoverflow.com/questions/2202461/yield-multiple-objects-at-a-time- from-an-iterable-object/2202485# 2202485 –

+0

jeśli odpowiedzi tutaj są niewystarczające, to blogowałem to tutaj: http://telliott99.blogspot.com/2010/01/chunks-of-sequence-in-python.html – telliott99

+5

Chociaż bardzo intrygujące, ta technika musi być skierowana przeciwko podstawowa "czytelność" wartości Pythona! – Demis

Odpowiedz

82

iter() to iterator sekwencji. [x] * n tworzy listę zawierającą n ilość ilość x, tj. Listę o długości n, gdzie każdy element to x. *arg rozpakowuje sekwencję argumentów dla wywołania funkcji. Dlatego trzy razy przekazujesz ten sam iterator do zip() i za każdym razem pobiera element z iteratora.

x = iter([1,2,3,4,5,6,7,8,9]) 
print zip(x, x, x) 
7

iter(s) zwraca iterator dla s.

[iter(s)]*n tworzy listę n razy ten sam iterator dla s.

Tak więc, wykonując zip(*[iter(s)]*n), wyodrębnia element ze wszystkich trzech iteratorów z listy w kolejności. Ponieważ wszystkie iteratory są tym samym obiektem, po prostu grupuje listę w kawałkach n.

+4

Nie "iteratory z tej samej listy", ale "n razy ten sam obiekt iteratora". Różne obiekty iteratora nie współdzielą stanu, nawet jeśli są z tej samej listy. –

+0

Dzięki, poprawione. Rzeczywiście właśnie to "myślałem", ale napisałem coś innego. – sttwister

37

Inne świetne odpowiedzi i komentarze dobrze wyjaśniają role argument unpacking i zip().

Jak Ignacio i ujukatzel powiedzieć, przekazać do zip() trzech odniesień do tego samego iteratora i zip() sprawia, 3-krotki z całkowitymi w porządku-z każdym odniesieniu do iteracyjnej:

1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9 
^     ^    ^   
    ^    ^    ^
      ^    ^    ^

a ponieważ prosić o bardziej opisowym próbki kodu:

chunk_size = 3 
L = [1,2,3,4,5,6,7,8,9] 

# iterate over L in steps of 3 
for start in range(0,len(L),chunk_size): # xrange() in 2.x; range() in 3.x 
    end = start + chunk_size 
    print L[start:end] # three-item chunks 

Następujące wartości start i end:

[0:3) #[1,2,3] 
[3:6) #[4,5,6] 
[6:9) #[7,8,9] 

FWIW, można uzyskać ten sam rezultat z map() z początkowym argument None:

>>> map(None,*[iter(s)]*3) 
[(1, 2, 3), (4, 5, 6), (7, 8, 9)] 

więcej na zip() i map(): http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/

+12

To nie jest trzy kopie tego samego iteratora, to trzy razy ten sam obiekt iteratora :) –

5

Jedno słowo rady do korzystania zamek ten droga. Obetnie twoją listę, jeśli jej długość nie jest równomiernie podzielna. Aby obejść ten problem, można użyć wartości itertools.izip_longest, jeśli można akceptować wartości wypełnienia.Lub można użyć coś takiego:

def n_split(iterable, n): 
    num_extra = len(iterable) % n 
    zipped = zip(*[iter(iterable)] * n) 
    return zipped if not num_extra else zipped + [iterable[-num_extra:], ] 

wykorzystania:

for ints in n_split(range(1,12), 3): 
    print ', '.join([str(i) for i in ints]) 

Drukuje:

1, 2, 3 
4, 5, 6 
7, 8, 9 
10, 11 
+2

To jest już udokumentowane w przepisach 'itertools': http://docs.python.org/2/library/itertools.html#recipes' grouper'. Nie trzeba wymyślać na nowo koła – jamylak

21

myślę o jednej rzeczy, która jest nieodebrane we wszystkich odpowiedzi (chyba oczywiste dla tych, którzy znają iteratory), ale nie tak oczywiste dla innych jest -

Ponieważ mamy ten sam iterator, dostaje s skonsumowane, a pozostałe elementy są używane przez zip. Więc jeśli po prostu użyliśmy listy, a nie iteracji, np. .

l = range(9) 
zip(*([l]*3)) # note: not an iter here, the lists are not emptied as we iterate 
# output 
[(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8)] 

Korzystanie iterator, wyskakuje wartości i tylko utrzymuje pozostały dostępne, więc na zamek po 0 jest zużywana 1 jest dostępna, a następnie 2 i tak dalej. Bardzo subtelna rzecz, ale całkiem sprytna !!!

+0

+1, Uratowałeś mnie! Nie mogę uwierzyć, że inne odpowiedzi pominęły ten ważny szczegół, zakładając, że wszyscy o tym wiedzą. Czy możesz podać jakiekolwiek odniesienie do dokumentacji, która zawiera te informacje? –