Why does x,y = zip(*zip(a,b)) work in Python?

2019-01-08 05:23发布

OK I love Python's zip() function. Use it all the time, it's brilliant. Every now and again I want to do the opposite of zip(), think "I used to know how to do that", then google python unzip, then remember that one uses this magical * to unzip a zipped list of tuples. Like this:

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)

What on earth is going on? What is that magical asterisk doing? Where else can it be applied and what other amazing awesome things in Python are so mysterious and hard to google?

标签: python zip
6条回答
Juvenile、少年°
2楼-- · 2019-01-08 06:13

It doesn't always work:

>>> 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! I think it needs a skull to scare it into working:

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

In python3 I think you need

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

since zip now returns a generator function which is not False-y.

查看更多
劳资没心,怎么记你
3楼-- · 2019-01-08 06:14

The asterisk performs apply (as it's known in Lisp and Scheme). Basically, it takes your list, and calls the function with that list's contents as arguments.

查看更多
走好不送
4楼-- · 2019-01-08 06:18

Addendum to @bcherry's answer:

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

So it works not just with keyword arguments (in this strict sense), but with named arguments too (aka positional arguments).

查看更多
ら.Afraid
5楼-- · 2019-01-08 06:20

The asterisk in Python is documented in the Python tutorial, under Unpacking Argument Lists.

查看更多
我欲成王,谁敢阻挡
6楼-- · 2019-01-08 06:20

I'm extremely new to Python so this just recently tripped me up, but it had to do more with how the example was presented and what was emphasized.

What gave me problems with understanding the zip example was the asymmetry in the handling of the zip call return value(s). That is, when zip is called the first time, the return value is assigned to a single variable, thereby creating a list reference (containing the created tuple list). In the second call, it's leveraging Python's ability to automatically unpack a list (or collection?) return value into multiple variable references, each reference being the individual tuple. If someone isn't familiar with how that works in Python, it makes it easier to get lost as to what's actually happening.

>>> 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
查看更多
小情绪 Triste *
7楼-- · 2019-01-08 06:28

It's also useful for multiple args:

def foo(*args):
  print args

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

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

And, you can use double asterisk for keyword arguments and dictionaries:

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}

And of course, you can combine these:

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

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

Pretty neat and useful stuff.

查看更多
登录 后发表回答