为什么x,y = zip(* zip(a,b))在Python中有效?

时间:2010-03-24 20:51:31

标签: python zip

好的我喜欢Python的zip()功能。一直使用它,它很棒。我一次又一次地想要做zip()的反面,想想“我以前知道怎么做”,然后谷歌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中其他令人惊叹的精彩内容是如此神秘且难以谷歌?

7 个答案:

答案 0 :(得分:39)

Python中的星号记录在Python教程的Unpacking Argument Lists下。

答案 1 :(得分:18)

星号执行apply(在Lisp和Scheme中已知)。基本上,它接受您的列表,并使用该列表的内容作为参数调用该函数。

答案 2 :(得分:8)

它对多个args也很有用:

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}

非常整洁有用的东西。

答案 3 :(得分: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的生成器函数。

答案 4 :(得分: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

答案 5 :(得分:0)

@ bcherry答案的附录:

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

因此,它不仅可以使用关键字参数(在this strict sense中),还可以使用命名参数(也就是位置参数)。

答案 6 :(得分:0)

(x, y) == tuple(zip(*zip(x,y)))为真,且仅当以下两个语句为真时:

  • xy的长度相同
  • xy是元组

了解发生了什么的一种好方法是在每个步骤打印:

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')]

基本上就是这样:

    来自xy
  1. 项目根据它们各自的索引进行配对。
  2. 对被解压成3个不同的对象(元组)
  3. 将对传递到zip,zip将再次根据索引对每个项目进行配对:
    • 所有输入的第一项配对:(1, 2, 3)
    • 来自所有输入的
    • 第二项配对:('a', 'b', 'c')

现在您可以了解为什么(x, y) == tuple(zip(*zip(x,y)))在这种情况下为假:

  • 由于yx长,因此第一个zip操作从y中删除了多余的项目(因为无法配对),因此显然在第二个项目上重新执行了此更改压缩操作
  • 类型不同,一开始我们有两个列表,现在我们有两个元组,因为zip确实在元组而不是列表中配对项目

如果您不确定100%地了解zip的工作原理,那么我在这里写了一个对此问题的答案:Unzipping and the * operator