Каков наиболее эффективный метод конкатенации строк в python?

Есть ли какой-либо эффективный метод конкатенации массовой строки в Python (например, StringBuilder в С# или StringBuffer в Java)? Я нашел следующие методы здесь:

  • Простая конкатенация с использованием +
  • Использование списка строк и метода join
  • Использование UserString из MutableString модуля
  • Использование символьного массива и модуля array
  • Использование cStringIO из StringIO модуля

Но что вы используете или предлагаете вам эксперты и почему?

[Связанный вопрос здесь]

Ответ 1

Вам может быть интересно: Анекдот оптимизации от Guido. Хотя стоит также помнить, что это старая статья, и она предшествует существованию таких вещей, как ''.join (хотя я думаю, string.joinfields более или менее одинаковый)

В силу этого модуль array может быть самым быстрым, если вы можете включить в него свою проблему. Но ''.join, вероятно, достаточно быстр и имеет преимущество быть идиоматичным и, таким образом, легче понять другим программистам на Python.

Наконец, золотое правило оптимизации: не оптимизируйте, если вы не знаете, что вам нужно, и измерьте, а не гадать.

Вы можете измерить различные методы с помощью модуля timeit. Это может сказать вам, что быстрее, а не случайные незнакомцы в Интернете, делая догадки.

Ответ 2

''.join(sequenceofstrings) - это то, что обычно работает лучше всего - простейшее и быстрое.

Ответ 3

Python 3.6 изменил игру для конкатенации строк известных компонентов с помощью Literal String Interpolation.

Учитывая тестовый пример от mkoistinen ответа, имея строки

domain = 'some_really_long_example.com'
lang = 'en'
path = 'some/really/long/path/'

Претенденты

  • f'http://{domain}/{lang}/{path}' - 0,151 мкс

  • 'http://%s/%s/%s' % (domain, lang, path) - 0,321 мкс

  • 'http://' + domain + '/' + lang + '/' + path - 0,356 мкс

  • ''.join(('http://', domain, '/', lang, '/', path)) - 0,249 мкс (обратите внимание, что построение кортежа постоянной длины немного быстрее, чем создание списка постоянной длины)).

Таким образом, в настоящее время самый короткий и самый красивый код также самый быстрый.

В альфа-версиях Python 3.6 реализация строк f'' была самой медленной из возможных - фактически сгенерированный байт-код в значительной степени эквивалентен ''.join() с ненужными вызовами str.__format__ который без аргументов просто возвращал бы self без изменений. Эти недостатки были устранены до финальной версии 3.6.

Скорость можно сравнить с самым быстрым методом для Python 2, который + конкатенация на моем компьютере; и это занимает 0,203 мкс с 8-битными строками и 0,259 мкс, если все строки являются Unicode.

Ответ 4

Это зависит от того, что вы делаете.

После Python 2.5 конкатенация строк с помощью оператора + довольно быстро. Если вы просто конкатенируете пару значений, лучше всего работает оператор +:

>>> x = timeit.Timer(stmt="'a' + 'b'")
>>> x.timeit()
0.039999961853027344

>>> x = timeit.Timer(stmt="''.join(['a', 'b'])")
>>> x.timeit()
0.76200008392333984

Однако, если вы собираете строку в цикле, вам лучше использовать метод объединения списка:

>>> join_stmt = """
... joined_str = ''
... for i in xrange(100000):
...   joined_str += str(i)
... """
>>> x = timeit.Timer(join_stmt)
>>> x.timeit(100)
13.278000116348267

>>> list_stmt = """
... str_list = []
... for i in xrange(100000):
...   str_list.append(str(i))
... ''.join(str_list)
... """
>>> x = timeit.Timer(list_stmt)
>>> x.timeit(100)
12.401000022888184

... но обратите внимание, что перед тем, как разница станет заметной, вы должны собрать относительно большое количество строк.

Ответ 5

В соответствии с ответом Джона Фухи, не оптимизируйте, если вам не нужно, но если вы здесь и задаете этот вопрос, это может быть именно потому, что вам нужно. В моем случае мне нужно было собрать несколько URL-адресов из строковых переменных... быстро. Я заметил, что никто (пока) не рассматривает метод строкового формата, поэтому я подумал, что попробую это и, в основном, для умеренного интереса, я подумал, что я бы бросил оператор интерполяции строк там для хорошего измерителя. Честно говоря, я не думал, что любой из них уложится в прямую операцию "+" или ".join(). Но угадайте, что? В моей системе Python 2.7.5 оператор интерполяции строк управляет ими всеми, а string.format() - худший исполнитель:

# concatenate_test.py

from __future__ import print_function
import timeit

domain = 'some_really_long_example.com'
lang = 'en'
path = 'some/really/long/path/'
iterations = 1000000

def meth_plus():
    '''Using + operator'''
    return 'http://' + domain + '/' + lang + '/' + path

def meth_join():
    '''Using ''.join()'''
    return ''.join(['http://', domain, '/', lang, '/', path])

def meth_form():
    '''Using string.format'''
    return 'http://{0}/{1}/{2}'.format(domain, lang, path)

def meth_intp():
    '''Using string interpolation'''
    return 'http://%s/%s/%s' % (domain, lang, path)

plus = timeit.Timer(stmt="meth_plus()", setup="from __main__ import meth_plus")
join = timeit.Timer(stmt="meth_join()", setup="from __main__ import meth_join")
form = timeit.Timer(stmt="meth_form()", setup="from __main__ import meth_form")
intp = timeit.Timer(stmt="meth_intp()", setup="from __main__ import meth_intp")

plus.val = plus.timeit(iterations)
join.val = join.timeit(iterations)
form.val = form.timeit(iterations)
intp.val = intp.timeit(iterations)

min_val = min([plus.val, join.val, form.val, intp.val])

print('plus %0.12f (%0.2f%% as fast)' % (plus.val, (100 * min_val / plus.val), ))
print('join %0.12f (%0.2f%% as fast)' % (join.val, (100 * min_val / join.val), ))
print('form %0.12f (%0.2f%% as fast)' % (form.val, (100 * min_val / form.val), ))
print('intp %0.12f (%0.2f%% as fast)' % (intp.val, (100 * min_val / intp.val), ))

Результаты:

# python2.7 concatenate_test.py
plus 0.360787868500 (90.81% as fast)
join 0.452811956406 (72.36% as fast)
form 0.502608060837 (65.19% as fast)
intp 0.327636957169 (100.00% as fast)

Если я использую более короткий домен и более короткий путь, интерполяция по-прежнему выигрывает. Разница более выражена, однако, с более длинными строками.

Теперь, когда у меня был хороший тест script, я также тестировал Python 2.6, 3.3 и 3.4, здесь результаты. В Python 2.6 лучший оператор - самый быстрый! На Python 3 соединение выигрывает. Примечание: эти тесты очень повторяемы в моей системе. Итак, "plus" всегда быстрее на 2.6, "intp" всегда быстрее на 2.7, а "join" всегда быстрее на Python 3.x.

# python2.6 concatenate_test.py
plus 0.338213920593 (100.00% as fast)
join 0.427221059799 (79.17% as fast)
form 0.515371084213 (65.63% as fast)
intp 0.378169059753 (89.43% as fast)

# python3.3 concatenate_test.py
plus 0.409130576998 (89.20% as fast)
join 0.364938726001 (100.00% as fast)
form 0.621366866995 (58.73% as fast)
intp 0.419064424001 (87.08% as fast)

# python3.4 concatenate_test.py
plus 0.481188605998 (85.14% as fast)
join 0.409673971997 (100.00% as fast)
form 0.652010936996 (62.83% as fast)
intp 0.460400978001 (88.98% as fast)

# python3.5 concatenate_test.py
plus 0.417167026084 (93.47% as fast)
join 0.389929617057 (100.00% as fast)
form 0.595661019906 (65.46% as fast)
intp 0.404455224983 (96.41% as fast)

Извлеченный урок:

  • Иногда мои предположения ошибочны.
  • Протестировать систему. вы будете работать на производстве.
  • Строковая интерполяция еще не мертва!

TL;DR:

  • Если вы используете 2.6, используйте оператор +.
  • Если вы используете 2.7, используйте оператор "%".
  • Если вы используете 3.x, используйте '.join().

Ответ 6

этот url имеет сопоставление различных подходов наряду с некоторым бенчмаркингом:

http://skymind.com/~ocrow/python_string/


Обратите внимание: Это очень старое сравнение с до 2009 года на основе Python 2.2, и поэтому в большинстве случаев следует игнорировать.

Ответ 7

это в значительной степени зависит от относительных размеров новой строки после каждого нового конкатенации. При использовании оператора + для каждой конкатенации создается новая строка. Если промежуточные строки относительно длинны, + становится все медленнее, потому что сохраняется новая промежуточная строка.

Рассмотрим этот случай:

from time import time
stri=''
a='aagsdfghfhdyjddtyjdhmfghmfgsdgsdfgsdfsdfsdfsdfsdfsdfddsksarigqeirnvgsdfsdgfsdfgfg'
l=[]
#case 1
t=time()
for i in range(1000):
    stri=stri+a+repr(i)
print time()-t

#case 2
t=time()
for i in xrange(1000):
    l.append(a+repr(i))
z=''.join(l)
print time()-t

#case 3
t=time()
for i in range(1000):
    stri=stri+repr(i)
print time()-t

#case 4
t=time()
for i in xrange(1000):
    l.append(repr(i))
z=''.join(l)
print time()-t

Результаты

1 0,00493192672729

2 0.000509023666382

3 0.00042200088501

4 0.000482797622681

В случае 1 & 2 мы добавляем большую строку, а join() выполняет примерно в 10 раз быстрее. В случае 3 & 4 мы добавляем небольшую строку, а "+" выполняет несколько быстрее

Ответ 8

Я столкнулся с ситуацией, когда мне нужно было иметь добавленную строку неизвестного размера. Это результаты тестов (python 2.7.3):

$ python -m timeit -s 's=""' 's+="a"'
10000000 loops, best of 3: 0.176 usec per loop
$ python -m timeit -s 's=[]' 's.append("a")'
10000000 loops, best of 3: 0.196 usec per loop
$ python -m timeit -s 's=""' 's="".join((s,"a"))'
100000 loops, best of 3: 16.9 usec per loop
$ python -m timeit -s 's=""' 's="%s%s"%(s,"a")'
100000 loops, best of 3: 19.4 usec per loop

Это, по-видимому, показывает, что "+ =" является самым быстрым. Результаты от ссылки skymind немного устарели.

(Я понимаю, что второй пример не является полным, окончательный список нужно будет соединить. Это показывает, однако, что просто подготовка списка занимает больше времени, чем строка concat.)

Ответ 9

Один год спустя, пусть тест mkoistinen ответит на python 3.4.3:

  • плюс 0,963564149000 (95,83% с такой скоростью)
  • присоединитесь к 0.923408469000 (100.00% быстро)
  • форма 1.501130934000 (61.51% с такой скоростью)
  • intp 1.019677452000 (на 90,56% быстрее)

Ничего не изменилось. Присоединение по-прежнему является самым быстрым методом. Поскольку intp является, пожалуй, лучшим выбором с точки зрения удобочитаемости, вы, возможно, захотите использовать intp.

Ответ 10

Вдохновленный критериями @JasonBaker, здесь прост, сравнивающий строки 10 "abcdefghijklmnopqrstuvxyz", показывающие, что .join() быстрее; даже при этом небольшом увеличении переменных:

катенация

>>> x = timeit.Timer(stmt='"abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz" + "abcdefghijklmnopqrstuvxyz"')
>>> x.timeit()
0.9828147209324385

Регистрация

>>> x = timeit.Timer(stmt='"".join(["abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz", "abcdefghijklmnopqrstuvxyz"])')
>>> x.timeit()
0.6114138159765048

Ответ 11

Для небольшого набора коротких строк (т.е. 2 или 3 строки длиной не более нескольких символов) плюс все еще быстрее. Использование mkoistinen замечательно script в Python 2 и 3:

plus 2.679107467004 (100.00% as fast)
join 3.653773699996 (73.32% as fast)
form 6.594011374000 (40.63% as fast)
intp 4.568015249999 (58.65% as fast)

Поэтому, когда ваш код выполняет огромное количество отдельных небольших конкатенаций, плюс является предпочтительным способом, если скорость имеет решающее значение.

Ответ 12

Вероятно, "новые f-строки в Python 3.6" - самый эффективный способ объединения строк.

Используя% s

>>> timeit.timeit("""name = "Some"
... age = 100
... '%s is %s.' % (name, age)""", number = 10000)
0.0029734770068898797

Использование .format

>>> timeit.timeit("""name = "Some"
... age = 100
... '{} is {}.'.format(name, age)""", number = 10000)
0.004015227983472869

Используя f

>>> timeit.timeit("""name = "Some"
... age = 100
... f'{name} is {age}.'""", number = 10000)
0.0019175919878762215

Источник: https://realpython.com/python-f-strings/