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?
答案 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)
答案 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)