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

Muh*_*uri 61 python types sum built-in

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

def sum2(iterable, start=0):
    return start + reduce(operator.add, iterable)
Run Code Online (Sandbox Code Playgroud)

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

 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
Run Code Online (Sandbox Code Playgroud)

为什么特别遗漏了字符串?

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

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

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

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

rbp*_*rbp 49

Python试图阻止你"汇总"字符串.你应该加入他们:

"".join(list_of_strings)
Run Code Online (Sandbox Code Playgroud)

它的速度要快得多,并且使用的内存要少得多.

快速基准:

$ 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
Run Code Online (Sandbox Code Playgroud)

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

顺便说一句,这种限制已经"永远"存在,即,因为它sum被添加为内置函数(rev.32347)

  • 我认为它是存在的,因为实现的时间顺序:当他们实现`sum`内置时,他们已经有了'join`字符串.因此,为了避免人们在不知情的情况下(或者有意识地,但懒洋洋地,顽皮地)使用`sum`作为字符串,他们特别禁止它.由于列表(或其他类型)没有特定的"总和",因此它们仅对字符串进行了例外处理.如今,我认为他们会保持这种方式,即使有人想出了具体的"总和",也是为了向后兼容. (2认同)

u0b*_*6ae 27

实际上sum(..),如果使用适当的起始对象,您可以使用连接字符串!当然,如果你走到这一步,你已经理解了足够的使用"".join(..)..

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

  • Python检查字符串仍然很奇怪,但对于列表或元组却没有. (9认同)
  • 我发现这非常有趣.当然它太聪明了一半,但它增加了对这个功能的理解. (4认同)

HS.*_*HS. 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);
    }
Run Code Online (Sandbox Code Playgroud)

那么..这是你的答案.

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

  • 看到代码很有意思,但问题是"*为什么*不是字符串求和",而不是"*如何*它们是否排除了求和的字符串?" ... (5认同)
  • 嗯,我的意思是,它不工作的原因是因为它在代码中被明确禁止.其他人似乎通过解释为什么你不应该这样做来回答. (4认同)

unu*_*tbu 14

来自文档:

连接字符串序列的首选快速方法是调用'.join(sequence).

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

  • @ThomasAhle:有一个计算原因,为什么`sum`是错误的工具.`sum`逐步构建结果.使用字符串执行此操作需要(可能很多)临时不可变字符串.如果天真地完成,这个过程需要大量的临时内存和大量的复制.相反,如果使用`''.join()`,则从头开始分配适当的内存量,并立即在那里构建结果.请参阅[Martelli的劝告](http://stackoverflow.com/questions/1349311/python-string-join-is-faster-than-but-whats-wrong-here/1350289#1350289). (3认同)
  • 当然,但你可以在Python中做很多低效的事情,而且通常没关系.我想不出任何其他情况,而不是"字符串总和",其中有一些测试反对做某事,在Python C代码中硬编码! (2认同)

dan*_*n04 11

简短回答:效率.

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

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

doubles总是大小相同,这使得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²).

因此,summing字符串将比summing数字慢得多.

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

  • 这个论点是错误的,因为列表和元组也是如此,可以求和.如HS所述,Python显式检查字符串,*仅*表示字符串,这没有意义. (4认同)
  • @Philipp:遗漏的部分是许多人试图使用`sum`作为字符串,并且没有多少人使用`sum`作为列表和元组.陷阱是`sum`适用于短字符串列表,但随后会生成列表可能很大的生产,并且性能会慢下来.这是一个常见的陷阱,在此实例中决定忽略duck-typing,并且不允许字符串与`sum`一起使用. (4认同)

Eth*_*man 10

之所以

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

关于为什么str不被允许的缺失部分是sum许多人试图sum用于字符串,并且没有多少sum用于列表和元组以及其他O(n**2)数据结构.该陷阱sum适用于短字符串列表,但随后可以投入生产,其中列表可能很大,并且性能会慢慢爬行.这是一个常见的陷阱,在此实例中决定忽略duck-typing,并且不允许使用字符串sum.

  • 有什么理由为什么`sum()`实现不只是调用`''.join()`当`sum()`遇到一个字符串而不是返回一个错误指示你调用'''.join ()`? (9认同)