Python sum, почему не строки?
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 по этой причине, поэтому объяснение или ссылка на поток, объясняющий это, было бы хорошо.
Edit: Я знаю, что стандартный способ-это сделать "".join
. Мой вопрос заключается в том, почему опция использования sum для строк была запрещена, и никакого запрета не было, скажем, для списков.
Edit 2: хотя я считаю, что это не нужно учитывая все хорошие ответы я получил, вопрос: почему произведение суммы на число повторяемое содержащие или содержащие списки повторяемое, но не повторяемое, содержащих строки?
8 ответов:
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
Edit (чтобы ответить на редактирование OP): что касается того, почему строки были, по-видимому, "выделены", я считаю, что это просто вопрос оптимизации для общего случая, а также применения наилучшей практики: вы можете присоединить строки намного быстрее с ".join, поэтому явно запрещая строки на
sum
укажу на это новичкам.кстати, это ограничение было на месте "вечно", т. е. с
sum
была добавлена в качестве встроенной функции (откр. 32347)
вы можете на самом деле использовать
sum(..)
для объединения строк, Если вы используете соответствующий начальный объект! Конечно, если вы зайдете так далеко, вы уже поняли достаточно, чтобы использовать"".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); }
Так.. вот тебе и ответ.
он явно проверяется в коде и отклоняется.
С документы:
предпочтительный, быстрый способ конкатенации a последовательность строк осуществляется путем вызова ".соединение (последовательность).
делая
sum
отказаться от работы со строками, Python призвал вас использовать правильный метод.
короткий ответ: эффективность.
длинный ответ:
sum
функция должна создать объект для каждой частичной суммы.предположим, что время, необходимое для создания объекта, прямо пропорционально размеру его данных. Пусть N обозначает количество элементов в последовательности для суммирования.
double
s всегда одинакового размера, что делаетsum
' s время работы O (1)×N = O (N).
int
(ранее известная какlong
) является произвольная длина. Пусть M обозначает абсолютное значение самого большого элемента последовательности. Тогдаsum
в худшем случае время работы составляет lg(M) + lg(2M) + lg (3M) + ... + ЛГ(Нм) = Н×ЛГ(м) + LG с(Н!) = O (N log N).на
str
(где M = длина самой длинной строки), в худшем случае время выполнения составляет M + 2M + 3M + ... + NM = M×(1 + 2 + ... + N)= O (N2).таким образом,
sum
мин строки будут намного медленнее, чемsum
Минг числа.
str.join
не выделяет никаких промежуточных объектов. Он предварительно выделяет буфер, достаточно большой для хранения Соединенных строк, и копирует строковые данные. Он работает в O (N) время, гораздо быстрее, чемsum
.
Почему
@dan04 имеет отличное объяснение стоимости использования
sum
на больших списках строк.недостающая часть о том, почему
str
Не допускаетсяsum
Это много, много людей пытались использоватьsum
для строк, и не многие используютsum
для списков и кортежей и других структур данных O(n**2). Ловушка в том, чтоsum
работает просто отлично для коротких списков строк, но затем попадает в производство, где списки могут быть огромными,и производительность замедляется до обхода. Это была такая распространенная ловушка, что было принято решение игнорировать утиный ввод в этом случае и не позволять использовать строки сsum
.
Edit: переместил части о неизменности в историю.
в основном, это вопрос предварительного распределения. При использовании оператора типа
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
, однако имеет задание выяснить реальный размер строки до ее запуска и поэтому теоретически будет выделять только один раз, в начале, а затем просто заполнить эту новую строку, что намного дешевле, чем другое решение.