Python 嵌套列表的内置总和

Python built-in sum for nested list

当我完成 leetcode 1313 时,我发现内置 sum 函数的特殊用法。

Leetcode 问题 1313

我们得到一个 nums 整数列表,表示用 运行 长度编码压缩的列表。

考虑每对相邻的元素 [a, b] = [nums[2*i], nums[2*i+1]] (with i >= 0)。对于每个这样的对,在解压缩列表中有 a 个值为 b 的元素。

Return解压后的列表。

示例 1:

Input: nums = [1,2,3,4]
Output: [2,4,4,4]
Explanation: The first pair [1,2] means we have freq = 1 and val = 2 so we generate the array [2].
The second pair [3,4] means we have freq = 3 and val = 4 so we generate [4,4,4].
At the end the concatenation [2] + [4,4,4,4] is [2,4,4,4].

有一个解决方案使用sum

nums = [1,2,3,4]
g = ([b] * a for a, b in zip(nums[::2], nums[1::2]))
print(list(g))
g = ([b] * a for a, b in zip(nums[::2], nums[1::2]))
print(sum(g,[]))

输出:

[[2], [4, 4, 4]]
[2, 4, 4, 4]

我的问题

我不知道为什么 sum 可以在这种情况下处理嵌套列表。任何人都可以告诉我吗?或者其他一些函数的行为是这样的?

这是内置 sumofficial guide

sum(foo) 简单地使用 + 的定义作为初始值。默认情况下,初始值为 0,因此 sum(g) 对于列表将失败,因为未定义列表和整数的添加。通过传递 [] 的显式初始值,这会强制 sum(foo, []) 等于 foo[0] + foo[1] + ... + foo[n-1] + [],如观察到的那样。

>>> sum([[1], [2]])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>> sum([[1], [2]], [])
[1, 2]

此定义的例外情况是您不能将 sumstr 值列表一起使用,即使您将 "" 指定为初始值也是如此。这是一个硬编码异常,导致 TypeError 并建议改用 str.join

>>> sum(["foo", "bar"])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> sum(["foo", "bar"], "")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sum() can't sum strings [use ''.join(seq) instead]

简答

给定的代码片段 运行s 连续列表连接。

工作原理

内置的sum()函数大致是这样工作的:

def sum(iterable, /, start=0):
    total = start
    for x in iterable:
        total = total + x
    return total

+ 运算符在左侧操作数上调用 __add__,因此 3 + 4 运行 与 (3).__add__(4) 一样,是对整数的加法运算。同样,[10, 20] + [30, 40, 50] 运行s 作为 [10, 20].__add__([30, 40, 50]),列表的串联操作。

这是给定示例中的结果:

>>> nums = [1,2,3,4]
>>> g = ([b] * a for a, b in zip(nums[::2], nums[1::2]))
>>> result = []
>>> x = next(g)
>>> result = result + x
>>> result
[2]
>>> x = next(g)
>>> result = result + x
>>> result
[2, 4, 4, 4]

为什么这不是一个好主意

连续列表连接在每次添加后生成下一个列表,因此它们 运行 以 O(n**2) 速度,这意味着这是一种二次算法,在给定大输入时 运行 速度过慢。

更好的方法

无需在每一步构建新列表,只需 extend the base list in-place. This runs at O(n) 线性速度:

>>> nums = [1,2,3,4]
>>> g = ([b] * a for a, b in zip(nums[::2], nums[1::2]))
>>> result = []                 # new list
>>> for x in g:
        result.extend(x)        # extend in-place

>>> result
[2, 4, 4, 4]

更好的方法

itertools module provides a tool for chaining together iterators。这使得问题变得很简单:

>>> nums = [1,2,3,4]
>>> g = ([b] * a for a, b in zip(nums[::2], nums[1::2]))
>>> list(chain.from_iterable(g))
[2, 4, 4, 4]

这个解决方案简短、快速,即使输入量很大也能很好地工作。