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 to zip(*[iter(s)]*n)
działa? Jak by to wyglądało, gdyby zostało napisane z bardziej szczegółowym kodem?
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 to zip(*[iter(s)]*n)
działa? Jak by to wyglądało, gdyby zostało napisane z bardziej szczegółowym kodem?
Odpowiedzi:
iter()
jest iteratorem po sekwencji. [x] * n
tworzy listę zawierającą n
ilość x
, czyli listę długości n
, w której znajduje się każdy element x
. *arg
rozpakowuje sekwencję na argumenty dla wywołania funkcji. Dlatego przekazujesz ten sam iterator 3 razy do zip()
i za każdym razem pobiera on element z iteratora.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
yield
s (= return
s) elementem, możesz sobie wyobrazić ten przedmiot jako „zużyty”. Więc następnym razem, gdy iterator jest wywoływany, zwraca następny „niewykorzystany” element.
Inne świetne odpowiedzi i komentarze dobrze wyjaśniają rolę rozpakowywania argumentów i zip () .
Jak mówią Ignacio i Ujukatzel , przechodzisz do zip()
trzech odniesień do tego samego iteratora i tworzyszzip()
3-krotki liczb całkowitych - po kolei - z każdego odniesienia do iteratora:
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ż prosisz o bardziej szczegółowy przykład 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
Zgodnie z wartościami start
i end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, możesz uzyskać ten sam wynik z map()
początkowym argumentem 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/
Myślę, że jedna rzecz, której brakuje we wszystkich odpowiedziach (prawdopodobnie oczywista dla osób znających iteratory), ale nie jest tak oczywista dla innych, to -
Ponieważ mamy ten sam iterator, zostaje on zużyty, a pozostałe elementy są używane przez zip. Jeśli więc po prostu skorzystaliśmy z listy, a nie z iter, 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)]
Użycie iteratora powoduje wyskakiwanie wartości i pozostanie tylko dostępnymi, więc dla zip, gdy zostanie zużyte 0, dostępne jest 1, a następnie 2 i tak dalej. Bardzo subtelna rzecz, ale całkiem sprytna !!!
iter(s)
zwraca iterator dla s.
[iter(s)]*n
tworzy listę n razy taką samą iterator dla s.
Tak więc, kiedy to robi zip(*[iter(s)]*n)
, wyodrębnia element ze wszystkich trzech iteratorów z listy po kolei. Ponieważ wszystkie iteratory są tym samym obiektem, po prostu grupuje listę w fragmenty n
.
Jedna rada dotycząca korzystania z zip w ten sposób. Lista zostanie obcięta, jeśli jej długość nie jest podzielna. Aby obejść ten problem, możesz użyć itertools.izip_longest, jeśli akceptujesz wartości wypełnienia. Lub możesz użyć czegoś 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:], ]
Stosowanie:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
Wydruki:
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
itertools
przepisach: docs.python.org/2/library/itertools.html#recipes grouper
. Nie ma potrzeby
Prawdopodobnie łatwiej jest zobaczyć, co się dzieje w interprecie Pythona lub ipython
z n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Mamy więc listę dwóch iteratorów, które wskazują na ten sam obiekt iteratora. Pamiętaj, że iter
na obiekcie zwraca obiekt iteratora, aw tym scenariuszu jest to ten sam iterator dwa razy ze względu na *2
cukier składniowy Pythona. Iteratory również działają tylko raz.
Ponadto zip
pobiera dowolną liczbę iterowalnych ( sekwencje są iterowalne ) i tworzy krotkę z i-tego elementu każdej z sekwencji wejściowych. Ponieważ oba iteratory są identyczne w naszym przypadku, zip przesuwa ten sam iterator dwa razy dla każdej 2-elementowej krotki wyjścia.
In [41]: help(zip)
Help on built-in function zip in module __builtin__:
zip(...)
zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.
Operator unpacking ( *
) zapewnia, że iteratory działają do wyczerpania, co w tym przypadku ma miejsce do momentu, gdy nie ma wystarczającej ilości danych wejściowych, aby utworzyć 2-elementową krotkę.
Można to rozszerzyć na dowolną wartość n
i zip(*[iter(s)]*n)
działa zgodnie z opisem.
*
to po prostu wygoda kopiowania obiektu. Wypróbuj to ze skalarami, a następnie z listami. Spróbuj także print(*zip(*[iter("ABCDEFG")]*2))
vs print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Następnie zacznij rozdzielać te dwa na mniejsze kroki, aby zobaczyć, jakie są faktycznie obiekty iteratora w dwóch instrukcjach.