【问题标题】:What does the built-in function sum do with sum(list, [])?内置函数 sum 对 sum(list, []) 有什么作用?
【发布时间】:2019-06-14 02:21:26
【问题描述】:

当我想展开列表时,我找到了如下方式:

>>> a = [[1, 2], [3, 4], [5, 6]]
>>> a
[[1, 2], [3, 4], [5, 6]]
>>> sum(a, [])
[1, 2, 3, 4, 5, 6]

我不知道这些行发生了什么,the documentation 表示:

sum(iterable[, start])

从左到右对startiterable 的项目求和 返回总数。 start 默认为 0。可迭代的项目是 通常为数字,start 的值不允许为字符串。

对于某些用例,sum() 有很好的替代方案。连接字符串序列的首选快速方法是调用 ''.join(sequence)。使用扩展添加浮点值 精度,见math.fsum()。连接一系列可迭代对象, 考虑使用itertools.chain()

2.3 版中的新功能。

你不认为 start 应该是一个数字吗?为什么[]可以写在这里?

(sum(a, []))

【问题讨论】:

    标签: python


    【解决方案1】:

    你不认为开始应该是一个数字吗?

    start 一个数字,默认情况下; 0,根据您引用的文档。因此,当你这样做时:

    sum((1, 2))
    

    它被评估为0 + 1 + 2,它等于3,每个人都很高兴。如果您想从不同的号码开始,您可以提供该号码:

    >>> sum((1, 2), 3)
    6
    

    到目前为止,一切都很好。


    但是,您可以在其他方面使用 +,例如列表:

    >>> ['foo'] + ['bar']
    ['foo', 'bar']
    

    如果您尝试为此使用sum,但期待相同的结果,您会得到TypeError

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

    因为它现在正在执行0 + ['foo'] + ['bar']

    要解决这个问题,您可以将自己的start 提供为[],这样它就变成了[] + ['foo'] + ['bar'],一切都很好了。所以回答:

    为什么[]可以写在这里?

    因为虽然start 默认为一个数字,但它必须为一;也可以添加其他东西,这对于与您当前正在做的事情完全一样非常有用。

    【讨论】:

    • sum 实际上被编码为拒绝字符串。它给出了错误TypeError: sum() can't sum strings [use ''.join(seq) instead]
    • @interjay 是的,只是在编辑 OP 时发现了这一点并进行了相应修复
    • 谢谢,我想我已经明白了。 :-)
    • 但请注意 - 这需要二次方时间!由于sum 始终使用+,如果您尝试将其用于列表连接,它将为每个中间连接构建一个新的中间列表,这需要二次复制。
    • @NeilG:问题是它仍然需要在每个+ 上重新分配。除非您被允许就地操作,否则将大小加倍并不会节省任何重新分配,并且不允许sum 就地操作。一个潜在的改进是使用+ 进行第一次添加,使用+= 进行后续添加,因为破坏第一个+ 的结果可能是可以的。不过,我不确定这是否会引起问题,而且我怀疑这样的改变会得到很多支持。你会得到像 sum([[], ()], []) 工作和 sum([(), []], []) 失败这样的奇怪现象。
    【解决方案2】:

    首先,永远不要使用sum 来连接/展平列表,因为它是二次时间的,因此与其他方法相比根本没有效率。它实际上使用了schlemiel the painter algorithm

    sum 函数在每次迭代时调用start__add__ 属性,其中所有可迭代项都作为第一个参数传递。

    例如:

    >>> [].__add__([2,3])
    [2, 3]
    #OR
    >>> [] + [1,2,3]
    [1, 2, 3]
    

    在这种情况下,结果将是您的输入列表的串联列表。从算法的角度来看,它执行以下操作:

    >>> a = [[1, 2], [3, 4], [5, 6]]
    >>> start = []
    >>> for i in a:
    ...     start += i
    ... 
    >>> start
    [1, 2, 3, 4, 5, 6]
    

    并不是说您可以在具有__add__ 属性的任何对象序列上调用sum 函数,但请注意,由于默认start 参数是0,如果您的对象不是整数,它将引发一个TypeError。在这种情况下,您需要为该函数指定一个正确的start

    >>> class newObj(object):
    ...    def  __init__(self,val):
    ...         self.val = val
    ...    def __add__(self,item):
    ...        return '{}_____{}'.format(self.val,item)
    ... 
    >>> 
    >>> start=newObj('new_obj')
    >>> 
    >>> start
    <__main__.newObj object at 0x7f75f9241c50>
    >>> 
    >>> start + 5
    'new_obj_____5'
    >>> 
    >>> 
    >>> sum(['1','2','3'],start)
    'new_obj_____123'
    

    【讨论】:

    • 谢谢,我想我已经明白了。 :-)
    【解决方案3】:

    您将start 与您作为第一个参数提供的可迭代的内容相加。 sum 不会将 typestart 限制为 int,以便允许各种添加情况。

    基本上 sum 是这样的:

    a = [[1, 2], [3, 4], [5, 6]]
    sum(a, number)
    

    大致翻译为:

    number += every value in the list a
    

    由于列表 a 中的每个值都是一个有效的列表,并且之前的总和在展开时看起来像这样:

    number + [1, 2] + [3, 4] + [5, 6]
    

    因此,如果您输入int,这将导致不幸的TypeError,因为不允许添加intlist

    1 + [1, 2] == I hope you like TypeErrors
    

    但是,如果您输入一个列表 [],它只会将 a 的元素连接在一起,从而生成我们熟悉和喜爱的扁平列表。

    start 的值默认为0int,主要是因为求和最常见的情况是算术。

    【讨论】:

    • 谢谢,我想我已经明白了。 :-)
    猜你喜欢
    • 2019-06-01
    • 2017-12-29
    • 2021-11-13
    • 2015-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    相关资源
    最近更新 更多