【问题标题】:Why does x,y = zip(*zip(a,b)) work in Python?为什么 x,y = zip(*zip(a,b)) 在 Python 中工作?
【发布时间】:2021-03-04 16:12:15
【问题描述】:

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

【问题讨论】:

  • 哦,是的。但这正是问题所在,在 stackoverflow 中搜索 zip(* python 不会在第一页返回重复的问题,而谷歌搜索 python *python zip(* 不会返回太多我猜是因为 (* 被忽略了?你是对的,其他人也认为这很棒。我应该删除问题吗?
  • 我不会删除它,因为出于某种原因它在搜索中的排名更高。关闭它将允许它用作重定向。
  • 我通过搜索“site:docs.python.org asterisk”找到了答案中提供的链接。对于搜索引擎来说,“星号”这个词比实际的星号字符要容易得多。 :-)
  • “Python 中还有哪些令人惊叹的令人敬畏的东西如此神秘且难以用谷歌搜索?”查看:stackoverflow.com/questions/101268/hidden-features-of-python 以获得答案:)

标签: python


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    星号执行apply(在 Lisp 和 Scheme 中众所周知)。基本上,它会获取您的列表,并使用该列表的内容作为参数调用函数。

    【讨论】:

    • Python2 系列还有一个apply 功能,但我认为没有任何用例是* 无法涵盖的。我相信它已经从 Python3 中删除了
    • @gnibbler:已确认。 apply 列在 python.org/dev/peps/pep-0361 标题下 Warnings for features removed in Py3k:
    • Apply 只存在,因为星号是后来添加的。
    【解决方案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
      

      哎呀!我认为它需要一个头骨来吓唬它工作:

      >>> 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 转换为列表。
      【解决方案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】:

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

        基本上是这样的:

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

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

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

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

        【讨论】:

          【解决方案7】:

          @bcherry 回答的附录:

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

          所以它不仅适用于关键字参数(在this strict sense),也适用于命名参数(又名位置参数)。

          【讨论】:

            猜你喜欢
            • 2011-04-24
            • 2021-04-25
            • 2020-06-26
            • 1970-01-01
            • 1970-01-01
            • 2012-01-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多