Строки Python являются неизменяемыми и непрерывными.Первое означает, что они не могут быть изменены, а второе означает, что они хранятся в одном месте в памяти.Это отличается от, например, структуры данных веревки , где добавление данных является дешевой операцией, для которой нужно только сформировать новый узел для конца.Это означает, что операция конкатенации должна каждый раз копировать обе входные строки и что-то вроде total_str = total_str + m + "\n"
, поскольку +
является левой ассоциативно , копирует все total_str
дважды.Обычным решением является сохранение всех маленьких строк до полного завершения набора и использование str.join
для выполнения конкатенации за один проход.Это будет копировать каждую строку компонента только один раз, вместо геометрического (пропорционального квадрату) числа раз.Другой вариант, чтобы построить буфер по мере продвижения, это использовать io.StringIO
.Это даст вам файлоподобный объект, похожий на StringBuilder
в некоторых других языках, из которого вы можете извлечь окончательную строку.У нас также есть такие операции, как writelines
, которые могут принимать итерации, поэтому соединение может вообще не понадобиться.
Я полагаю, почему вторая реализация смогла быть намного быстрее (не только в два раза быстрее), что существуют оптимизации, которые иногда позволяют CPython не выполнять копию левого операндасовсем.PyUnicode_Append
, по-видимому, имеет именно такую оптимизацию, основанную на unicode_modifiable
, в которой он может мутировать объект, если счетчик ссылок равен точно 1, строка никогда не хэшировалась, и некоторые другие условия.Обычно это относится к локальной переменной, где вы используете +=
, и, вероятно, компилятору удалось сгенерировать такое поведение, когда в том же присваивании не было второго оператора.