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)
yield(= returns) элемент, вы можете представить этот элемент как «потребленный». Таким образом, при следующем вызове итератора он возвращает следующий «неиспользованный» элемент.
Другие отличные ответы и комментарии хорошо объясняют роль распаковки аргументов и zip () .
Как говорят Игнасио и Удзюкацель , вы переходите к zip()трем ссылкам на один и тот же итератор и создаете тройки zip()целых чисел - по порядку - из каждой ссылки на итератор:
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/
Я думаю, что одна вещь, упущенная во всех ответах (вероятно, очевидная для тех, кто знаком с итераторами), но не столь очевидная для других, - это -
Поскольку у нас один и тот же итератор, он потребляется, а остальные элементы используются архивом. Итак, если бы мы просто использовали список, а не его, например.
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
itertoolsрецептах: docs.python.org/2/library/itertools.html#recipes grouper . Не нужно изобретать велосипед
Вероятно, легче увидеть, что происходит в интерпретаторе python или ipythonс помощью n = 2:
In [35]: [iter("ABCDEFGH")]*2
Out[35]: [<iterator at 0x6be4128>, <iterator at 0x6be4128>]
Итак, у нас есть список из двух итераторов, которые указывают на один и тот же объект итератора. Помните, что iterобъект возвращает объект-итератор, и в этом сценарии это тот же итератор дважды из-за *2синтаксического сахара Python. Итераторы также запускаются только один раз.
Кроме того, zipпринимает любое количество итераций ( последовательности являются итерациями ) и создает кортеж из i-го элемента каждой из входных последовательностей. Поскольку в нашем случае оба итератора идентичны, zip перемещает один и тот же итератор дважды для каждого двухэлементного кортежа вывода.
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.
Оператор unpacking ( *) гарантирует, что итераторы работают до полного исчерпания, что в данном случае происходит до тех пор, пока не будет достаточно ввода для создания двухэлементного кортежа.
Это может быть расширено до любого значения nи zip(*[iter(s)]*n)работает, как описано.
*- это просто удобство дублирования объекта. Попробуйте это со скалярами, а затем со списками. Также попробуйте print(*zip(*[iter("ABCDEFG")]*2))против print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")])). Затем начните разбивать их на более мелкие шаги, чтобы увидеть, каковы на самом деле объекты-итераторы в двух операторах.