Есть причины не использовать '+' для объединения двух строк?


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

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

это хорошая практика, чтобы использовать + для объединения двух строк? Или есть проблема Я не в курсе?

7 104

7 ответов:

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

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

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

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

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

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

создайте два списка, один из связанных, так вопрос и еще больше сфабриковано

>>> 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 приводит к ненужным вызовам функций, которые могут добавить к накладным расходам.

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

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

  1. длина рассматриваемой строки
  2. нет операции конкатенации.

и вне курса в развитии преждевременная оптимизация-это зло.

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

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

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 может предложить.

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

import sys

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

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

и приурочен он:

mslade@mickpc:/binks/micks/ruby/tests$ time python /binks/micks/junk/strings.py  8000000
8000000 times

real    0m2.165s
user    0m1.620s
sys     0m0.540s
mslade@mickpc:/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) время, как можно было бы подозревать.

так, по крайней мере, с точки зрения производительности, используя + - это хорошо.

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

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

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

".join ([a, b]) лучше, чем +.

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

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

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