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
s (= return
s) елемент, ви можете уявити цей елемент як "спожитий". Отже, наступного разу, коли викликається ітератор, він видає наступний "неспоживаний" елемент.
Інші чудові відповіді та коментарі добре пояснюють ролі розпакування аргументів та zip () .
Як кажуть Ігнасіо і урукацель , ви переходите до 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-transposed-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)]
Використовуючи ітератор, з'являється значення і залишається доступним лише тоді, коли для поштового індексу один раз 0 споживається 1, а потім 2 і так далі. Дуже тонка річ, але досить розумна !!!
iter(s)
повертає ітератор для s.
[iter(s)]*n
складає список n разів у тому ж ітераторі для s.
Отже, виконуючи zip(*[iter(s)]*n)
це, він витягує елемент зі всіх трьох ітераторів зі списку по порядку. Оскільки всі ітератори є одним і тим же об'єктом, він просто групує список за шматками n
.
Одне слово поради щодо використання zip таким чином. Він уріже ваш список, якщо його довжина не розділиться рівномірно. Щоб вирішити це, ви можете використовувати itertools.izip_lo most, якщо ви можете прийняти значення заповнення. Або ви можете використовувати щось подібне:
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
синтаксичний цукор пітона. Ітератори також працюють лише один раз.
Далі 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)
працює, як описано.
*
- це просто зручність дублювання об'єкта. Спробуйте це зі скалярами, а потім зі списками. Також спробуйте print(*zip(*[iter("ABCDEFG")]*2))
проти print(*zip(*[iter("ABCDEFG"), iter("ABCDEFG")]))
. Тоді починайте розривати їх двома кроками, щоб побачити, що насправді є об'єктами ітератора у двох операторах.