Любая причина не использовать "+" для объединения двух строк?

Общим антипаттерном в Python является конкатенация последовательности строк с использованием + в цикле. Это плохо, потому что интерпретатор Python должен создать новый строковый объект для каждой итерации, и он заканчивается квадратичным временем. (Последние версии CPython могут, по-видимому, оптимизировать это в некоторых случаях, но другие реализации не могут, поэтому программистам не рекомендуется полагаться на это.) ''.join - правильный способ сделать это.

Однако, я слышал, что он сказал (в том числе здесь, о переполнении стека), что никогда не следует использовать + для конкатенации строк, но вместо этого всегда используйте ''.join или строку формата. Я не понимаю, почему это так, если вы только конкатенируете две строки. Если мое понимание верное, оно не должно принимать квадратичное время, и я думаю, что a + b более чистый и читаемый, чем либо ''.join((a, b)) или '%s%s' % (a, b).

Хорошо ли использовать +, чтобы объединить две строки? Или есть проблема, о которой я не знаю?

Ответ 1

Нет ничего плохого в объединении двух строк с +. Действительно, это легче читать, чем ''.join([a, b]).

Вы правы, хотя конкатенация более чем двух строк с + является операцией O (n ^ 2) (по сравнению с O (n) для join) и, таким образом, становится неэффективной. Однако это не связано с использованием цикла. Даже a + b + c + ... - O (n ^ 2), причина в том, что каждая конкатенация создает новую строку.

CPython2.4 и выше попытаются уменьшить это, но по-прежнему рекомендуется использовать join при конкатенации более 2 строк.

Ответ 2

Оператор

Plus - это прекрасное решение для конкатенации строк two Python. Но если вы продолжаете добавлять более двух строк (n > 25), вы можете подумать о чем-то еще.

''.join([a, b, c]) трюк - это оптимизация производительности.

Ответ 3

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

Скажем, функция требует аргумента, и вы пишете ее, ожидая получить строку:

In [1]: def foo(zeta):
   ...:     print 'bar: ' + zeta

In [2]: foo('bang')
bar: bang

Таким образом, эта функция может использоваться довольно часто во всем коде. Ваши коллеги могут точно знать, что он делает, но не обязательно полностью обновлять внутренние элементы и, возможно, не знают, что функция ожидает строку. И поэтому они могут закончиться этим:

In [3]: foo(23)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

/home/izkata/<ipython console> in foo(zeta)

TypeError: cannot concatenate 'str' and 'int' objects

Не было бы проблем, если бы вы использовали строку формата:

In [1]: def foo(zeta):
   ...:     print 'bar: %s' % zeta
   ...:     
   ...:     

In [2]: foo('bang')
bar: bang

In [3]: foo(23)
bar: 23

То же самое верно для всех типов объектов, которые определяют __str__, которые могут быть переданы также:

In [1]: from datetime import date

In [2]: zeta = date(2012, 4, 15)

In [3]: print 'bar: ' + zeta
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)

/home/izkata/<ipython console> in <module>()

TypeError: cannot concatenate 'str' and 'datetime.date' objects

In [4]: print 'bar: %s' % zeta
bar: 2012-04-15

Итак, да: если вы можете использовать строку формата, сделайте это и воспользуйтесь тем, что может предложить Python.

Ответ 4

Предполагается, что никогда не следует использовать + для конкатенации строк, но вместо этого всегда используйте ".join, возможно, миф. Верно, что использование + создает ненужные временные копии неизменяемого строкового объекта, но другой, не цитируемый факт, заключается в том, что вызов join в цикле обычно добавляет служебные данные function call. Давайте рассмотрим ваш пример.

Создайте два списка, один из связанных вопросов SO, а другой - более крупный сфабрикованный

>>> myl1 = ['A','B','C','D','E','F']
>>> myl2=[chr(random.randint(65,90)) for i in range(0,10000)]

Давайте создадим две функции UseJoin и UsePlus, чтобы использовать соответствующие функции join и +.

>>> def UsePlus():
    return [myl[i] + myl[i + 1] for i in range(0,len(myl), 2)]

>>> def UseJoin():
    [''.join((myl[i],myl[i + 1])) for i in range(0,len(myl), 2)]

Позволяет запустить timeit с первым списком

>>> myl=myl1
>>> t1=timeit.Timer("UsePlus()","from __main__ import UsePlus")
>>> t2=timeit.Timer("UseJoin()","from __main__ import UseJoin")
>>> print "%.2f usec/pass" % (1000000 * t1.timeit(number=100000)/100000)
2.48 usec/pass
>>> print "%.2f usec/pass" % (1000000 * t2.timeit(number=100000)/100000)
2.61 usec/pass
>>> 

У них почти одинаковое время выполнения.

Используется cProfile

>>> myl=myl2
>>> cProfile.run("UsePlus()")
         5 function calls in 0.001 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    0.001    0.001 <pyshell#1376>:1(UsePlus)
        1    0.000    0.000    0.001    0.001 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.000    0.000    0.000    0.000 {range}


>>> cProfile.run("UseJoin()")
         5005 function calls in 0.029 CPU seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.015    0.015    0.029    0.029 <pyshell#1388>:1(UseJoin)
        1    0.000    0.000    0.029    0.029 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {len}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     5000    0.014    0.000    0.014    0.000 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {range}

И похоже, что использование Join приводит к ненужным вызовам функций, которые могут добавить к служебным данным.

Теперь вернемся к вопросу. Следует ли избегать использования + над join во всех случаях?

Я считаю, что нет, все должно быть принято во внимание

  • Длина строки в вопросе
  • Нет операции конкатенации.

И внекурсник в разработке досрочной оптимизации - это зло.

Ответ 5

Я сделал быстрый тест:

import sys

str = e = "a xxxxxxxxxx very xxxxxxxxxx long xxxxxxxxxx string xxxxxxxxxx\n"

for i in range(int(sys.argv[1])):
    str = str + e

и приурочен к этому:

[email protected]:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  8000000
8000000 times

real    0m2.165s
user    0m1.620s
sys     0m0.540s
[email protected]:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  16000000
16000000 times

real    0m4.360s
user    0m3.480s
sys     0m0.870s

По-видимому, существует оптимизация для случая a = a + b. Он не показывает время O (n ^ 2), как можно было бы подозревать.

Итак, по крайней мере с точки зрения производительности, использование + в порядке.

Ответ 6

В соответствии с документами Python использование str.join() даст вам согласованность производительности для различных реализаций Python. Хотя CPython оптимизирует квадратичное поведение s = s + t, другие реализации Python могут не быть.

Детализация реализации CPython. Если s и t - обе строки, некоторые Реализации Python, такие как CPython, обычно могут выполнять на месте оптимизация для присвоений формы s = s + t или s + = t. когда применимая эта оптимизация делает квадратичное время выполнения намного меньше вероятно. Эта оптимизация - это и версия, и реализация зависимый. Для чувствительного к производительности кода предпочтительно использовать str.join(), который обеспечивает согласованную линейную конкатенацию производительность по версиям и реализациям.

Типы последовательностей в документах Python (см. примечание к ноге [6])

Ответ 7

'. join ([a, b]) - лучшее решение, чем +.

Потому что код должен быть написан таким образом, чтобы не ущемлять другие реализации Python (PyPy, Jython, IronPython, Cython, Psyco и т.д.)

form a + = b или = a + b является хрупким даже в CPython и вообще не присутствует в реализациях , которые не используют refcounting (ссылка подсчет - это метод хранения количества ссылок, указателей или дескрипторов ресурса, такого как объект, блок памяти, дисковое пространство или другой ресурс).

https://www.python.org/dev/peps/pep-0008/#programming-recommendations