Почему x, y = zip (* zip (a, b)) работают в Python?

OK Я люблю функцию Python zip(). Используйте его все время, это блестяще. Время от времени я хочу сделать противоположное от zip(), подумать "я знал, как это сделать", а затем google python unzip, а затем вспомнить, что он использует этот магический * для распаковки zip-списка кортежей. Вот так:

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?

Ответы

Ответ 2

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

Ответ 3

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

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}

Довольно аккуратный и полезный материал.

Ответ 4

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

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

Ответ 5

Я очень новичок в 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

Ответ 6

Добавление к ответу @bcherry:

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

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

Ответ 7

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