Есть причины не использовать '+' для объединения двух строк?
общий антипаттерн в Python заключается в объединении последовательности строк с помощью +
в цикле. Это плохо, потому что интерпретатор Python должен создавать новый строковый объект для каждой итерации, и это в конечном итоге занимает квадратичное время. (Последние версии CPython, по-видимому, могут оптимизировать это в некоторых случаях, но другие реализации не могут, поэтому программистам не рекомендуется полагаться на это.)''.join
это правильный способ сделать это.
тем не менее, я слышал, что он сказал (в том числе здесь, на переполнение стека), что вы должны никогда использовать +
для конкатенации строк, но вместо этого всегда используйте ''.join
или строка формата. Я не понимаю, почему это так, если вы объединяете только две строки. Если мое понимание правильно, это не должно занять квадратичное время, и я думаю a + b
чище и читабельнее, чем любой ''.join((a, b))
или '%s%s' % (a, b)
.
это хорошая практика, чтобы использовать +
для объединения двух строк? Или есть проблема Я не в курсе?
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 приводит к ненужным вызовам функций, которые могут добавить к накладным расходам.
теперь вернемся к вопросу. Следует ли препятствовать использованию
+
overjoin
во всех случаях?Я считаю, что нет, вещи должны быть приняты во внимание
- длина рассматриваемой строки
- нет операции конкатенации.
и вне курса в развитии преждевременная оптимизация-это зло.
при работе с несколькими людьми, иногда трудно точно знать, что происходит. Использование строки формата вместо конкатенации может избежать одного конкретного раздражения, которое произошло с нами целую тонну раз:
скажем, функция требует аргумента, и вы пишете его, ожидая получить строку:
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