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 ) 。
因此, sum
string将比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)