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)]
Как работает zip(*[iter(s)]*n)
? Как бы это выглядело, если бы оно было написано с более подробным кодом?
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)]
Как работает zip(*[iter(s)]*n)
? Как бы это выглядело, если бы оно было написано с более подробным кодом?
iter()
является итератором над последовательностью. [x] * n
создает список, содержащий n
количество x
, т.е. список длины n
, где каждый элемент x
. *arg
распаковывает последовательность в аргументы для вызова функции. Поэтому вы пропустите один и тот же итератор 3 раза до zip()
, и каждый раз он извлекает элемент из итератора.
x = iter([1,2,3,4,5,6,7,8,9])
print zip(x, x, x)
Другие замечательные ответы и комментарии хорошо объясняют роли распаковка аргументов и zip().
Как Ignacio и ujukatzel скажите, что вы переходите на zip()
три ссылки на один и тот же итератор и zip()
делает 3-кортежи целых чисел по порядку - от каждой ссылки на итератор:
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
^ ^ ^
^ ^ ^
^ ^ ^
И поскольку вы запрашиваете более подробный пример кода:
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
Следуя значениям start
и end
:
[0:3) #[1,2,3]
[3:6) #[4,5,6]
[6:9) #[7,8,9]
FWIW, вы можете получить тот же результат с map()
с начальным аргументом None
:
>>> map(None,*[iter(s)]*3)
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
Подробнее о zip()
и map()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/
Я думаю, что одна вещь, которая пропустила во всех ответах (вероятно, очевидная для тех, кто знаком с итераторами), но не столь очевидная для других, -
Так как у нас тот же самый итератор, он потребляется, а остальные элементы используются zip. Поэтому, если мы просто использовали список, а не iter например.
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)]
Используя итератор, выдает значения и остается только доступным, поэтому для zip один раз 0 потребляется 1, а затем 2 и так далее. Очень тонкая вещь, но довольно умная!!!
iter(s)
возвращает итератор для s.
[iter(s)]*n
составляет список n раз один и тот же итератор для s.
Таким образом, при выполнении zip(*[iter(s)]*n)
он извлекает элемент из всех трех итераторов из списка по порядку. Поскольку все итераторы являются одним и тем же объектом, он просто группирует список в кусках n
.
Одно слово совета для использования zip таким образом. Он усечет ваш список, если длина не будет делимой. Чтобы обойти это, вы можете использовать itertools.izip_longest, если вы можете принять значения заполнения. Или вы можете использовать что-то вроде этого:
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:], ]
Использование:
for ints in n_split(range(1,12), 3):
print ', '.join([str(i) for i in ints])
Печать
1, 2, 3
4, 5, 6
7, 8, 9
10, 11
Вероятно, легче увидеть, что происходит в интерпретаторе python или ipython
с n = 2
:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Итак, у нас есть список из двух итераторов, которые указывают на один и тот же объект-итератор. Помните, что iter
на объекте возвращает объект итератора, и в этом случае он является тем же самым итератором дважды из-за синтаксического сахара *2
python. Итераторы запускаются только один раз.
Кроме того, zip
принимает любое число итераций (последовательности являются итерабельными) и создает кортеж из i-го элемента каждой из входных последовательностей. Поскольку оба итератора в нашем случае одинаковы, zip перемещает один и тот же итератор дважды для каждого 2-элементного кортежа вывода.
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.
Оператор распаковки (*
) гарантирует, что итераторы будут исчерпаны, что в этом случае до тех пор, пока не будет достаточно ввода для создания двухэлементного кортежа.
Это можно расширить до любого значения n
и zip(*[iter(s)]*n)
как описано.