Python总和,为什么不是字符串?

时间:2010-08-19 19:13:31

标签: python types sum built-in

Python有一个内置函数sum,它实际上等同于:

def sum2(iterable, start=0):
    return start + reduce(operator.add, iterable)

除了字符串之外的所有类型的参数。它适用于数字和列表,例如:

 sum([1,2,3], 0) = sum2([1,2,3],0) = 6    #Note: 0 is the default value for start, but I include it for clarity
 sum({888:1}, 0) = sum2({888:1},0) = 888

为什么特别遗漏字符串?

 sum( ['foo','bar'], '') # TypeError: sum() can't sum strings [use ''.join(seq) instead]
 sum2(['foo','bar'], '') = 'foobar'

我似乎记得在Python列表中讨论的原因,因此解释或解释它的线程的链接会很好。

修改:我知道标准方法是"".join。我的问题是为什么禁止对字符串使用sum的选项,并且没有禁止使用例如列表。

编辑2 :虽然我认为这不是必需的,但我得到了所有好的答案,问题是:为什么sum对包含数字的迭代或包含列表的迭代起作用但是不是包含字符串的iterable?

8 个答案:

答案 0 :(得分:49)

Python试图阻止你“汇总”字符串。你应该加入他们:

"".join(list_of_strings)

速度更快,使用的内存更少。

快速基准:

$ python -m timeit -s 'import operator; strings = ["a"]*10000' 'r = reduce(operator.add, strings)'
100 loops, best of 3: 8.46 msec per loop
$ python -m timeit -s 'import operator; strings = ["a"]*10000' 'r = "".join(strings)'
1000 loops, best of 3: 296 usec per loop

编辑(回答OP的编辑):至于为什么字符串显然被“挑出”,我认为这只是优化常见案例以及实施最佳实践的问题:你可以更快地加入字符串''.join,所以明确禁止sum上的字符串会将其指向新手。

顺便说一下,这个限制已经“永远”存在,即,因为sum被添加为内置函数(rev. 32347

答案 1 :(得分:27)

如果使用适当的起始对象,实际上可以使用sum(..)来连接字符串!当然,如果你走得这么远,你已经足够了解"".join(..)了。

>>> class ZeroObject(object):
...  def __add__(self, other):
...   return other
...
>>> sum(["hi", "there"], ZeroObject())
'hithere'

答案 2 :(得分:17)

以下是来源:http://svn.python.org/view/python/trunk/Python/bltinmodule.c?revision=81029&view=markup

在builtin_sum函数中,我们有一些代码:

     /* reject string values for 'start' parameter */
        if (PyObject_TypeCheck(result, &PyBaseString_Type)) {
            PyErr_SetString(PyExc_TypeError,
                "sum() can't sum strings [use ''.join(seq) instead]");
            Py_DECREF(iter);
            return NULL;
        }
        Py_INCREF(result);
    }

所以..那是你的答案。

在代码中明确检查并拒绝。

答案 3 :(得分:14)

来自the docs

  

连接的首选,快速方法   字符串序列是通过调用   ''。加入(序列)。

通过让sum拒绝对字符串进行操作,Python鼓励您使用正确的方法。

答案 4 :(得分:11)

简短回答:效率。

答案很长:sum函数必须为每个部分和创建一个对象。

假设创建对象所需的时间与其数据的大小成正比。设N表示序列中要求求和的元素数。

double的大小始终相同,这使sum的运行时间为O(1)×N = O(N)

int(以前称为long)是任意长度的。设M表示最大序列元素的绝对值。那么sum的最坏情况运行时间是lg(M)+ lg(2M)+ lg(3M)+ ... + lg(NM)= N×lg(M)+ lg(N!) = O(N log N)

对于str(其中M =最长字符串的长度),最坏情况下的运行时间为M + 2M + 3M + ... + NM = M×(1 + 2 + ... + N)= O(N²)

因此,sum ming字符串将比sum ming数字慢得多。

str.join不分配任何中间对象。它预分配一个足够大的缓冲区来保存连接的字符串,并复制字符串数据。它以 O(N)时间运行,比sum快得多。

答案 5 :(得分:10)

原因

@ dan04对在大型字符串列表中使用sum的成本有很好的解释。

str不允许使用sum的原因是许多人尝试使用sum表示字符串,而很少有人使用sum用于列表和元组以及其他O(n ** 2)数据结构。陷阱是sum适用于短字符串列表,但随后可以投入生产,其中列表可能很大,并且性能会慢慢爬行。这是一个常见的陷阱,在此实例中决定忽略duck-typing,并且不允许字符串与sum一起使用。

答案 6 :(得分:4)

修改:将有关不变性的部分移至历史记录。

基本上,它是预分配的问题。使用

等语句时
sum(["a", "b", "c", ..., ])

并期望它与reduce语句类似,生成的代码类似于

v1 = "" + "a" # must allocate v1 and set its size to len("") + len("a")
v2 = v1 + "b" # must allocate v2 and set its size to len("a") + len("b")
...
res = v10000 + "$" # must allocate res and set its size to len(v9999) + len("$")

在每个步骤中都会创建一个新字符串,当字符串变得越来越长时,可能会产生一些复制开销。但这可能不是重点。更重要的是,每行上的每个新字符串必须分配到它的特定大小(我不知道它必须在reduce语句的每次迭代中分配,可能有一些明显的启发式方法可供使用,Python可能会在这里和那里分配更多用于重用 - 但在几个点上新的字符串将足够大,这将不再有用,Python必须再次分配,这是相当昂贵的。

join之类的专用方法,但它有一个工作,可以在字符串开始之前计算字符串的实际大小,因此理论上只在开始时分配一次,然后只填充新字符串,比其他解决方案便宜得多。

答案 7 :(得分:3)

我不知道为什么,但这有效!

import operator
def sum_of_strings(list_of_strings):
    return reduce(operator.add, list_of_strings)