【问题标题】:list() vs iterable unpacking in Python 3.5+Python 3.5+ 中的 list() 与可迭代解包
【发布时间】:2019-03-03 12:21:24
【问题描述】:

在支持后者的 Python 版本中,list(iterable)[*iterable] 之间有什么实际区别吗?

【问题讨论】:

  • 特殊情况:可以将名称 list 重新绑定到内置类型以外的其他名称,但不能更改 [*iterable] 语法的含义。

标签: python python-3.x list iterable python-internals


【解决方案1】:

显然在 CPython 中存在性能差异,其中 [*a] 过度分配而 list() 没有:What causes [*a] to overallocate?

【讨论】:

    【解决方案2】:

    做同样事情的两个结构之间总是会有一些差异。问题是,我不会说这种情况下的差异实际上是实用。两者都是接受可迭代的表达式,遍历它,然后从中创建一个列表。

    合同是一样的:输入是一个可迭代的输出是一个由可迭代元素填充的列表。

    是的,list 可以反弹为不同的名称; list(it) 是函数调用,[*it] 是列表显示; [*it] 使用较小的迭代器更快,但通常使用较大的迭代器执行相同的操作。哎呀,甚至可以考虑到[*it] 减少了三下击键这一事实。

    这些实用吗?在尝试从可迭代列表中获取列表时,我会想到它们吗?好吧,也许是为了保持在 79 个字符以下并让 linter 将其关闭的击键。

    【讨论】:

      【解决方案3】:

      由于 [*iterable] 正在解包,它接受 assignment-like 语法,这与 list(iterable) 不同:

      >>> [*[]] = []
      >>> list([]) = []
        File "<stdin>", line 1
      SyntaxError: can't assign to function call
      

      您可以阅读有关此here 的更多信息(虽然没用)。

      您也可以使用list(sequence=iterable),即带有关键字参数:

      >>> list(sequence=[])
      []
      

      再次not useful

      【讨论】:

      • 虽然可以将[*var] = iterable 写成var = list(iterable),但从可读性的角度来看,即使是对高尔夫编码并不陌生的人也难以理解。 [*var] = iterablevar = [*iterable] 之间还有一个有趣的对称性(尽管在 (*var,) = iterablevar = (*iterable,) 的情况下被破坏了)。
      • @RomanOdaisky 确实,*lst, = (1,2,2); print(lst) 是我想的最简单的,但仍然不推荐使用
      • 实际上,我找到了一个有效的用例:for keys, [*rows] in itertools.groupby(...):!
      【解决方案4】:

      您可以使用标准库模块dis 来调查函数生成的字节码。在这种情况下:

      import dis
      
      def call_list(x):
          return list(x)
      
      def unpacking(x):
          return [*x]
      
      dis.dis(call_list)
      #   2           0 LOAD_GLOBAL              0 (list)
      #               2 LOAD_FAST                0 (x)
      #               4 CALL_FUNCTION            1
      #               6 RETURN_VALUE
      
      dis.dis(unpacking)
      #   2           0 LOAD_FAST                0 (x)
      #               2 BUILD_LIST_UNPACK        1
      #               4 RETURN_VALUE
      

      所以有一个区别,不仅是加载全局定义的名称list,解包时不需要发生这种情况。所以归结为内置的list函数是如何定义的以及BUILD_LIST_UNPACK究竟做了什么。

      请注意,两者实际上都比为此编写标准列表理解要少得多:

      def list_comp(x):
          return [a for a in x]
      
      dis.dis(list_comp)
      #   2           0 LOAD_CONST               1 (<code object <listcomp> at 0x7f65356198a0, file "<ipython-input-46-dd71fb182ec7>", line 2>)
      #               2 LOAD_CONST               2 ('list_comp.<locals>.<listcomp>')
      #               4 MAKE_FUNCTION            0
      #               6 LOAD_FAST                0 (x)
      #               8 GET_ITER
      #              10 CALL_FUNCTION            1
      #              12 RETURN_VALUE
      

      【讨论】:

        【解决方案5】:

        list(x) 是一个函数,[*x] 是一个表达式。你可以重新分配list,并让它做其他事情(但你不应该这样做)。

        说到 cPython,b = list(a) 翻译成这个字节码序列:

        LOAD_NAME                1 (list)
        LOAD_NAME                0 (a)
        CALL_FUNCTION            1
        STORE_NAME               2 (b)
        

        相反,c = [*a] 变为:

        LOAD_NAME                0 (a)
        BUILD_LIST_UNPACK        1
        STORE_NAME               3 (c)
        

        所以你可以争辩说[*a] 可能会稍微高效一些,但也有一点点。

        【讨论】:

          猜你喜欢
          • 2019-06-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-18
          • 1970-01-01
          • 1970-01-01
          • 2012-05-07
          • 1970-01-01
          相关资源
          最近更新 更多