Python总结,为什么不是string?

Python有一个内置的函数sum ,它实际上相当于:

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

除string外的所有types的参数。 它适用于数字和列表,例如:

  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 

为什么string被特意排除?

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

我似乎记得在Python列表中讨论的原因,所以解释或链接到解释它的线程将是好的。

编辑 :我知道,标准的方法是做"".join 。join。 我的问题是为什么禁止使用string的选项,并且没有禁止列表。

编辑2 :虽然我相信这不是所需的所有好的答案,我得到的问题是: 为什么总和工作在一个包含数字或迭代包含列表的迭代,但不是一个包含string的迭代?

Python试图阻止你“求和”string。 你应该join他们:

 "".join(list_of_strings) 

速度要快得多,而且使用的内存也less得多。

一个快速的基准:

 $ 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的编辑):至于为什么string显然被“挑出来”,我相信这只是一个优化的常见问题,以及执行最佳实践的问题:你可以更快的joinstring。join,所以明确禁止和弦上的string将指出这对新手。

顺便说一下,这个限制已经“永远”了,也就是说,因为这个sum是作为一个内置函数join的( rev.33234 )

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

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

这是来源: 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); } 

所以..这是你的答案。

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

从文档 :

连接string序列的首选方法是调用''.join(sequence)。

通过对string进行sum拒绝操作,Python鼓励你使用正确的方法。

简短的回答:效率。

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

假设创build对象所需的时间量与其数据的大小成正比。 设N代表序列中元素的总和。

double s总是相同的大小,这使得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日志N)

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

因此, sumstring将比sum数字慢得多。

str.join不分配任何中间对象。 它预先分配一个足够容纳连接string的缓冲区,并复制string数据。 它运行在O(N)时间,比sum快得多。

之所以

@ dan04对使用大量string列表的成本有很好的解释。

关于为什么str不允许sum的缺失部分是许多人试图使用sum作为string,并没有多less使用sum列表和元组以及其他O(n ** 2)数据结构。 陷阱是, sum对于短的string列表来说工作得很好,但是随后被放到了列表可能很大的生产中,并且性能变慢了。 这是一个常见的陷阱,在这种情况下做出的决定是忽略鸭式打字,并且不允许使用string。

编辑:移动关于不变性的部分到历史。

基本上,它是一个预分配的问题。 当你使用诸如。的语句时

 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("$") 

在每一个这些步骤中都会创build一个新的string,当string越来越长时,这个string可能会给一些复制开销。 但这可能不是重点。 更重要的是,每一行上的每一个新string都必须分配给它的特定大小(我不知道它是否必须在reduce语句的每一次迭代中分配,可能会有一些明显的启发式用法,并且Python可能会在这里和那里分配多一点的重用 – 但在几个点上,新的string将足够大,这不会再有帮助,Python必须再次分配,这是相当昂贵的。

然而像join这样的专用方法的作用是在开始之前计算出string的实际大小,因此理论上在开始时只分配一次,然后只填充新的string,这比其他解决scheme便宜得多。

我不知道为什么,但这个作品!

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