Зачем быть.'' join () быстрее, чем += в Python?
Я могу найти множество информации в интернете (о переполнении стека и в противном случае) о том, как это очень неэффективно и плохо использовать + или += для конкатенации в Python.
Я не могу понять, почему += неэффективно. Помимо упоминания здесь ,что" он был оптимизирован для улучшения 20% в некоторых случаях " (все еще не ясно, что это за случаи), я не могу найти никакой дополнительной информации.
что происходит на более техническом уровень, который делает ''.join() превосходит другие методы конкатенации Python?
2 ответа:
допустим, у вас есть этот код, чтобы построить строку из трех строк:
x = 'foo' x += 'bar' # 'foobar' x += 'baz' # 'foobarbaz'в этом случае Python сначала должен выделить и создать
'foobar'прежде чем он может выделить и создать'foobarbaz'.для каждого
+=это вызывается, все содержимое строки и все, что добавляется к ней, должно быть скопировано в совершенно новый буфер памяти. Другими словами, если у вас естьNстроки, которые будут объединены, вам нужно выделить примерноNвременные строки и первая подстрока копируется ~N раз. Последняя подстрока копируется только один раз, но в среднем каждая подстрока копируется~N/2раза.С
.join, Python может играть несколько трюков, так как промежуточные строки не нужно создавать. CPython выясняет, сколько памяти ему нужно спереди, а затем выделяет буфер правильного размера. Наконец, затем он копирует каждую часть в новый буфер, что означает, что каждый кусок копируется только один раз.
есть и другие жизнеспособные подходы, которые могут привести к повышению производительности для
+=в некоторых случаях. Например, если внутреннее строковое представление на самом деле являетсяropeили если среда выполнения на самом деле достаточно умна, чтобы каким-то образом выяснить, что временные строки бесполезны для программы и оптимизировать их.однако CPython, безусловно, делает не делают ли эти оптимизации надежно (хотя это может быть для несколько случаев углу) и поскольку это наиболее распространенная реализация в использовании, многие лучшие практики основаны на том, что хорошо работает для CPython. Наличие стандартизированного набора норм также делает его легче для других реализаций, чтобы сосредоточиться, а также их усилия по оптимизации.
Я думаю, что это поведение лучше всего объяснить в глава строкового буфера Lua.
чтобы переписать это объяснение в контексте Python, давайте начнем с невинного фрагмента кода (производного от одного из документов Lua):
s = "" for l in some_list: s += lпредположим, что каждый
l- это 20 байт иsуже был проанализирован до размера 50 КБ. Когда Python сцепляетs + lон создает новую строку с 50 020 байт и копирует 50 КБ изsв этот новый строка. То есть, для каждой новой строки, программа перемещает 50 кб памяти, и растет. После прочтения 100 новых строк (всего 2 КБ) фрагмент уже переместился более чем на 5 Мб памяти. Чтобы сделать вещи хуже, после назначенияs += lстарая строка теперь мусор. После двух циклов цикла есть две старые строки, составляющие в общей сложности более 100 КБ мусора. Таким образом, компилятор языка решает запустить свой сборщик мусора и освобождает эти 100 КБ. Проблема в том, что это будет происходит каждые два цикла, и программа будет запускать свой сборщик мусора две тысячи раз, прежде чем читать весь список. Даже при всей этой работе его использование памяти будет большим кратным размеру списка.
и в конце:
эта проблема не свойственна Lua: другие языки с истинным мусором коллекция, и где строки являются неизменяемыми объектами, представляют собой аналогичные поведение, Java является самым известным примером. (Java предлагает структура
StringBufferдля улучшения проблемы.)строки Python также неизменяемые объекты.