Хотя этот метод очень интригующий, он должен противоречить основному значению «читабельности» Python!
Демис
Ответы:
109
iter()является итератором по последовательности. [x] * nсоздает список, содержащий nколичество x, т.е. список длины n, в котором находится каждый элемент x. *argраспаковывает последовательность в аргументы для вызова функции. Поэтому вы передаете один и тот же итератор 3 раза zip(), и он каждый раз извлекает элемент из итератора.
Полезно знать: когда итератор использует yield(= returns) элемент, вы можете представить этот элемент как «потребленный». Таким образом, при следующем вызове итератора он возвращает следующий «неиспользованный» элемент.
Как говорят Игнасио и Удзюкацель , вы переходите к zip()трем ссылкам на один и тот же итератор и создаете тройки zip()целых чисел - по порядку - из каждой ссылки на итератор:
И поскольку вы просите более подробный пример кода:
chunk_size =3
L =[1,2,3,4,5,6,7,8,9]# iterate over L in steps of 3for start in range(0,len(L),chunk_size):# xrange() in 2.x; range() in 3.x
end = start + chunk_sizeprint 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:
Я думаю, что одна вещь, упущенная во всех ответах (вероятно, очевидная для тех, кто знаком с итераторами), но не столь очевидная для других, - это -
Поскольку у нас один и тот же итератор, он потребляется, а остальные элементы используются архивом. Итак, если бы мы просто использовали список, а не его, например.
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 и так далее. Очень тонкая вещь, но довольно умная !!!
+1, ты меня спас! Я не могу поверить, что в других ответах эта важная деталь была пропущена, если все это знают. Можете ли вы дать ссылку на документацию, которая включает эту информацию?
Снехасиш Кармакар
9
iter(s) возвращает итератор для s.
[iter(s)]*n составляет список n раз один и тот же итератор для s.
Таким образом, при выполнении zip(*[iter(s)]*n)он по порядку извлекает элемент из всех трех итераторов из списка. Поскольку все итераторы являются одним и тем же объектом, он просто группирует список по частям n.
Не n итераторов одного и того же списка, но n раз один и тот же объект итератора. Различные объекты итератора не имеют общего состояния, даже если они находятся в одном списке.
Thomas Wouters
Спасибо, поправили. Действительно, я «думал» об этом, но написал кое-что еще.
sttwister
6
Один совет по использованию zip таким образом. Он обрежет ваш список, если его длина не делится поровну. Чтобы обойти это, вы можете использовать itertools.izip_longest, если вы можете принимать значения заливки. Или вы можете использовать что-то вроде этого:
Вероятно, легче увидеть, что происходит в интерпретаторе python или ipythonс помощью n = 2:
In[35]:[iter("ABCDEFGH")]*2Out[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)работает, как описано.
Извините за медлительность. Но не могли бы вы объяснить «один и тот же итератор дважды из-за синтаксического сахара Python * 2. Итераторы также запускаются только один раз». расстаться пожалуйста? Если да, то почему результат не [(«А», «А») ....]? Спасибо.
Боуэн Лю
@BowenLiu *- это просто удобство дублирования объекта. Попробуйте это со скалярами, а затем со списками. Также попробуйте print(*zip(*[iter("ABCDEFG")]*2))против print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")])). Затем начните разбивать их на более мелкие шаги, чтобы увидеть, каковы на самом деле объекты-итераторы в двух операторах.
Ответы:
iter()
является итератором по последовательности.[x] * n
создает список, содержащийn
количествоx
, т.е. список длиныn
, в котором находится каждый элементx
.*arg
распаковывает последовательность в аргументы для вызова функции. Поэтому вы передаете один и тот же итератор 3 разаzip()
, и он каждый раз извлекает элемент из итератора.источник
yield
(=return
s) элемент, вы можете представить этот элемент как «потребленный». Таким образом, при следующем вызове итератора он возвращает следующий «неиспользованный» элемент.Другие отличные ответы и комментарии хорошо объясняют роль распаковки аргументов и zip () .
Как говорят Игнасио и Удзюкацель , вы переходите к
zip()
трем ссылкам на один и тот же итератор и создаете тройкиzip()
целых чисел - по порядку - из каждой ссылки на итератор:И поскольку вы просите более подробный пример кода:
Следуя значениям
start
иend
:FWIW, вы можете получить тот же результат с
map()
начальным аргументомNone
:Для получения дополнительной информации
zip()
иmap()
: http://muffinresearch.co.uk/archives/2007/10/16/python-transposing-lists-with-map-and-zip/источник
Я думаю, что одна вещь, упущенная во всех ответах (вероятно, очевидная для тех, кто знаком с итераторами), но не столь очевидная для других, - это -
Поскольку у нас один и тот же итератор, он потребляется, а остальные элементы используются архивом. Итак, если бы мы просто использовали список, а не его, например.
Используя итератор, выталкивает значения и сохраняет только оставшиеся доступные, поэтому для zip после использования 0 доступно 1, затем 2 и так далее. Очень тонкая вещь, но довольно умная !!!
источник
iter(s)
возвращает итератор для s.[iter(s)]*n
составляет список n раз один и тот же итератор для s.Таким образом, при выполнении
zip(*[iter(s)]*n)
он по порядку извлекает элемент из всех трех итераторов из списка. Поскольку все итераторы являются одним и тем же объектом, он просто группирует список по частямn
.источник
Один совет по использованию zip таким образом. Он обрежет ваш список, если его длина не делится поровну. Чтобы обойти это, вы можете использовать itertools.izip_longest, если вы можете принимать значения заливки. Или вы можете использовать что-то вроде этого:
Использование:
Печать:
источник
itertools
рецептах: docs.python.org/2/library/itertools.html#recipesgrouper
. Не нужно изобретать велосипедВероятно, легче увидеть, что происходит в интерпретаторе python или
ipython
с помощьюn = 2
:Итак, у нас есть список из двух итераторов, которые указывают на один и тот же объект итератора. Помните, что
iter
объект возвращает объект-итератор, и в этом сценарии это тот же итератор дважды из-за*2
синтаксического сахара Python. Итераторы также запускаются только один раз.Кроме того,
zip
принимает любое количество итераций ( последовательности являются итерациями ) и создает кортеж из i-го элемента каждой из входных последовательностей. Поскольку в нашем случае оба итератора идентичны, zip перемещает один и тот же итератор дважды для каждого двухэлементного кортежа вывода.Оператор unpacking (
*
) гарантирует, что итераторы работают до полного исчерпания, что в данном случае происходит до тех пор, пока не будет достаточно ввода для создания двухэлементного кортежа.Это может быть расширено до любого значения
n
иzip(*[iter(s)]*n)
работает, как описано.источник
*
- это просто удобство дублирования объекта. Попробуйте это со скалярами, а затем со списками. Также попробуйтеprint(*zip(*[iter("ABCDEFG")]*2))
противprint(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Затем начните разбивать их на более мелкие шаги, чтобы увидеть, каковы на самом деле объекты-итераторы в двух операторах.