Как zip (*[iter (s)]*n) работает в Python?
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)
работы? Как бы это выглядело, если бы он был написан с более подробным кодом?
6 ответов:
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. Поэтому, если мы просто использовали список, а не 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
принимает любое количество итераций (последовательности are iterables) и создает кортеж из 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.
The распаковка (
*
оператор) гарантирует, что итераторы работают до исчерпания, которое в этом случае до тех пор, пока не будет достаточно входных данных для создания 2-элементного кортежа.это может быть продлен к любому значению
n
иzip(*[iter(s)]*n)
работает, как описано.