Почему x, y = zip (* zip (a, b)) работает в Python? - PullRequest
75 голосов
/ 24 марта 2010

ОК. Мне нравится функция Python zip(). Используйте это все время, это блестяще. Время от времени я хочу сделать что-то противоположное 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 настолько загадочны и сложны для поиска в Google?

Ответы [ 7 ]

39 голосов
/ 24 марта 2010

Звездочка в Python описана в руководстве по Python под Распаковка списков аргументов .

18 голосов
/ 24 марта 2010

Звездочка выполняет apply (как известно в Лиспе и Схеме). По сути, он берет ваш список и вызывает функцию с содержимым этого списка в качестве аргументов.

8 голосов
/ 25 марта 2010

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

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 голосов
/ 27 апреля 2013

Это не всегда работает:

>>> 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

Oops! Я думаю, что нужен череп, чтобы напугать его до работы:

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

В python3 я думаю, что вам нужно

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

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

2 голосов
/ 25 февраля 2015

Я чрезвычайно новичок в 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 голосов
/ 03 апреля 2019

(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 работает, я написал ответ на этот вопрос здесь: Распаковка и оператор *

0 голосов
/ 12 мая 2014

Приложение к ответу @ bcherry:

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

Так что он работает не только с аргументами ключевых слов (в в строгом смысле ), но и с именованными аргументами (иначе говоря, позиционные аргументы).

...