Чому x, y = zip (* zip (a, b)) працює в Python?


79

Добре, мені подобається zip()функція Python . Використовуйте його постійно, це чудово. Час від часу я хочу зробити протилежне zip(), подумайте: "Раніше я знав, як це зробити", потім google python розпакуйте, потім пам'ятайте, що за допомогою цього чарівного *можна розпакувати заархівований список кортежів. Подобається це:

x = [1,2,3]
y = [4,5,6]
zipped = zip(x,y)
unzipped_x, unzipped_y = zip(*zipped)
unzipped_x
    Out[30]: (1, 2, 3)
unzipped_y
    Out[31]: (4, 5, 6)

Що це за діло відбувається? Що робить ця чарівна зірочка? Де ще його можна застосувати і які ще дивовижні чудові речі в Python такі таємничі та важкі для пошуку?



3
о так. Однак саме в цьому полягає проблема, пошук stackoverflow для zip(*python не повертає дублікат запитання на першій сторінці, а googling python *чи python zip(*не повертає багато, я думаю, тому що (*ігнорується? Ти маєш рацію, хоча хтось ще вважав це чудовим. Чи слід видаляти питання?
Mike Dewar

1
Я б не видаляв її, оскільки вона чомусь вища в пошуках. Закриття цього дозволить йому служити як переспрямування.
Джош Лі

5
Я знайшов посилання, вказане у моїй відповіді, за допомогою пошуку "site: docs.python.org зірочка". Слово "зірочка" набагато простіше для пошукових систем, ніж справжня зірочка. :-)
Даніель Штуцбах

4
"які ще дивовижні чудові речі в Python такі таємничі та важкі для пошуку в Google?" Перевірте: stackoverflow.com/questions/101268/hidden-features-of-python для відповіді :)
Адам Паркін

Відповіді:



18

Зірочка виконує apply(як це відомо в Lisp та Scheme). По суті, він бере ваш список і викликає функцію із вмістом цього списку як аргументи.


1
Серія Python2 все ще має applyфункцію, але я не думаю, що є випадки використання, на які неможливо охопити *. Я вважаю, що його було видалено з Python3
Джон Ла Рой

1
@gnibbler: Підтверджено. applyперелічено на python.org/dev/peps/pep-0361 під заголовкомWarnings for features removed in Py3k:
MatrixFrog

2
Застосування існує лише тому, що зірочку було додано пізніше.
DasIch

9

Це також корисно для кількох аргументів:

def foo(*args):
  print args

foo(1, 2, 3) # (1, 2, 3)

# also legal
t = (1, 2, 3)
foo(*t) # (1, 2, 3)

І ви можете використовувати подвійну зірочку для аргументів ключових слів та словників:

def foo(**kwargs):
   print kwargs

foo(a=1, b=2) # {'a': 1, 'b': 2}

# also legal
d = {"a": 1, "b": 2}
foo(**d) # {'a': 1, 'b': 2}

І звичайно, ви можете поєднати це:

def foo(*args, **kwargs):
   print args, kwargs

foo(1, 2, a=3, b=4) # (1, 2) {'a': 3, 'b': 4}

Досить акуратні та корисні речі.


6

Це не завжди працює:

>>> x = []
>>> y = []
>>> zipped = zip(x, y)
>>> unzipped_x, unzipped_y = zip(*zipped)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack

Ой! Я думаю, йому потрібен череп, щоб налякати його:

>>> unzipped_x, unzipped_y = zip(*zipped) or ([], [])
>>> unzipped_x
[]
>>> unzipped_y
[]

У python3 я думаю, що вам потрібно

>>> unzipped_x, unzipped_y = tuple(zip(*zipped)) or ([], [])

оскільки zip тепер повертає функцію генератора, яка не є False-y.


Або просто використовувати генератор unzipped_x = (z[0] for z in zipped). Якщо zippedсам є генератором, то спочатку перетворіть його у список, щоб ви могли ще раз переглядати файл for unzipped_y. Немає додаткових витрат порівняно з zip(*zipped)тим, що останній також перетворюється zippedна список у процесі розпакування аргументів.
Ian Goldby

2

Я надзвичайно новачок у Python, тому нещодавно це мене спокусило, але це мало більше стосуватись того, як був поданий приклад і що було наголошено.

Що мені дало проблеми з розумінням прикладу zip, це асиметрія в обробці повернутих значень виклику zip. Тобто, коли zip викликається вперше, повернене значення присвоюється одній змінній, тим самим створюючи посилання на список (що містить створений список кортежів). У другому виклику це використовує здатність Python автоматично розпаковувати повернене значення списку (або колекції?) У кілька посилань змінних, кожна посилання являє собою окремий кортеж. Якщо хтось не знайомий з тим, як це працює в Python, це полегшує загубитись щодо того, що насправді відбувається.

>>> x = [1, 2, 3]
>>> y = "abc"
>>> zipped = zip(x, y)
>>> zipped
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> z1, z2, z3 = zip(x, y)
>>> z1
(1, 'a')
>>> z2
(2, 'b')
>>> z3
(3, 'c')
>>> rezipped = zip(*zipped)
>>> rezipped
[(1, 2, 3), ('a', 'b', 'c')]
>>> rezipped2 = zip(z1, z2, z3)
>>> rezipped == rezipped2
True

0

Додаток до відповіді @ bcherry:

>>> def f(a2,a1):
...  print a2, a1
... 
>>> d = {'a1': 111, 'a2': 222}
>>> f(**d)
222 111

Отже, це працює не тільки з аргументами ключових слів (у цьому строгому сенсі ), але і з іменованими аргументами (вони ж позиційні аргументи).


0

(x, y) == tuple(zip(*zip(x,y))) є істинним тоді і лише тоді, коли відповідають два наступні твердження:

  • xі yмають однакову довжину
  • xі yє кортежами

Хороший спосіб зрозуміти, що відбувається, - це друкувати на кожному кроці:

x = [1, 2, 3]
y = ["a", "b", "c", "d"]

print("1) x, y = ", x, y)
print("2) zip(x, y) = ", list(zip(x, y)))
print("3) *zip(x, y) = ", *zip(x, y))
print("4) zip(*zip(x,y)) = ", list(zip(*zip(x,y))))

Які результати:

1) x, y =            [1, 2, 3] ['a', 'b', 'c', 'd']
2) zip(x, y) =       [(1, 'a'), (2, 'b'), (3, 'c')]
3) *zip(x, y) =       (1, 'a')  (2, 'b')  (3, 'c')
4) zip(*zip(x,y)) =  [(1, 2, 3), ('a', 'b', 'c')]

В основному так відбувається:

  1. Елементи, що входять до списків, xі yв парі відповідно до їх відповідних індексів.
  2. Пари розпаковуються до 3 різних предметів (кортежів)
  3. Пари передаються у zip-файл, який знову з’єднує всі елементи на основі індексів:
    • сполучені перші елементи з усіх входів: (1, 2, 3)
    • Другі елементи з усіх входів спарені: ('a', 'b', 'c')

Тепер ви можете зрозуміти, чому (x, y) == tuple(zip(*zip(x,y)))в цьому випадку неправда:

  • оскільки yдовше ніж x, перша операція zip видалила зайвий елемент з y(оскільки її не вдалося створити в парі), ця зміна очевидно впливає на другу операцію zip
  • типи відрізняються, спочатку у нас було два списки, тепер у нас є два кортежі, як zipі пара елементів у кортежах, а не в списках

Якщо ви не впевнені на 100%, щоб зрозуміти, як це zipпрацює, я написав відповідь на це запитання тут: Розпакування та оператор *

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.