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


78

好的,我喜欢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中还有哪些其他令人赞叹的令人敬畏的事物如此神秘且难以谷歌搜索?



3
哦耶。不过,这恰恰是问题所在,在stackoverflow上搜索zip(*python不会在第一页上返回重复的问题,而谷歌搜索python *python zip(*不会返回太多我想是因为(*忽略了吗?没错,其他人也认为这很棒。我应该删除问题吗?
Mike Dewar 2010年

1
我不会删除它,因为某些原因它在搜索中的排名较高。关闭它可以使其用作重定向。
Josh Lee 2010年

5
我通过搜索“ site:docs.python.org星号”找到了答案中提供的链接。对于搜索引擎而言,“星号”一词比实际的星号字符容易得多。:-)
Daniel Stutzbach 2010年

4
“ Python中还有哪些其他令人赞叹的令人敬畏的东西如此神秘且难以谷歌搜索?” 签出:stackoverflow.com/questions/101268/hidden-features-of-python 寻找答案:)
亚当·帕金

Answers:



18

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


1
Python2系列仍然具有一个apply功能,但我不认为不能涵盖任何用例*。我相信它已从Python3中删除
John La Rooy

1
@gnibbler:确认。apply列在python.org/dev/peps/pep-0361的标题下Warnings for features removed in Py3k:
MatrixFrog 2010年

2
仅存在应用是因为星号是在以后添加的。
DasIch 2010年

8

对于多个参数也很有用:

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本身是一个生成器,则首先将其转换为列表,以便您可以再次迭代unzipped_y。与之相比,没有其他成本,zip(*zipped)因为后者zipped在解压缩参数的过程中也转换为列表。
伊恩·高德比

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

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

  • 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. xy中的项目根据其各自的索引进行配对。
  2. 线对解包到3个不同的对象(元组)
  3. 将对传递到zip,zip将再次根据索引对每个项目进行配对:
    • 来自所有输入的第一项配对: (1, 2, 3)
    • 来自所有输入的第二项配对: ('a', 'b', 'c')

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

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

如果您不是100%不确定要如何zip工作,我在这里为这个问题写了一个答案:解压缩和*运算符

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.