Конкатенация строк и подстановка строк в Python
в Python, где и когда использование конкатенации строк против подстановки строк ускользает от меня. Поскольку конкатенация строк увидела большие повышения производительности, является ли это (становится больше) стилистическим решением, а не практическим?
для конкретного примера, как следует обрабатывать конструкцию гибких URIs:
DOMAIN = 'http://stackoverflow.com'
QUESTIONS = '/questions'
def so_question_uri_sub(q_num):
return "%s%s/%d" % (DOMAIN, QUESTIONS, q_num)
def so_question_uri_cat(q_num):
return DOMAIN + QUESTIONS + '/' + str(q_num)
Edit: также были предложения о присоединении к списку строк и использовании именованной подстановки. Это варианты центральная тема, которая заключается в том, какой путь является правильным способом сделать это в какое время? Спасибо за ответы!
9 ответов
конкатенация (значительно) быстрее в соответствии с моей машиной. Но стилистически я готов заплатить цену замены, если производительность не критична. Ну, и если мне нужно форматирование, нет необходимости даже задавать вопрос... нет другого выбора, кроме как использовать интерполяцию / шаблон.
>>> import timeit
>>> def so_q_sub(n):
... return "%s%s/%d" % (DOMAIN, QUESTIONS, n)
...
>>> so_q_sub(1000)
'http://stackoverflow.com/questions/1000'
>>> def so_q_cat(n):
... return DOMAIN + QUESTIONS + '/' + str(n)
...
>>> so_q_cat(1000)
'http://stackoverflow.com/questions/1000'
>>> t1 = timeit.Timer('so_q_sub(1000)','from __main__ import so_q_sub')
>>> t2 = timeit.Timer('so_q_cat(1000)','from __main__ import so_q_cat')
>>> t1.timeit(number=10000000)
12.166618871951641
>>> t2.timeit(number=10000000)
5.7813972166853773
>>> t1.timeit(number=1)
1.103492206766532e-05
>>> t2.timeit(number=1)
8.5206360154188587e-06
>>> def so_q_tmp(n):
... return "{d}{q}/{n}".format(d=DOMAIN,q=QUESTIONS,n=n)
...
>>> so_q_tmp(1000)
'http://stackoverflow.com/questions/1000'
>>> t3= timeit.Timer('so_q_tmp(1000)','from __main__ import so_q_tmp')
>>> t3.timeit(number=10000000)
14.564135316080637
>>> def so_q_join(n):
... return ''.join([DOMAIN,QUESTIONS,'/',str(n)])
...
>>> so_q_join(1000)
'http://stackoverflow.com/questions/1000'
>>> t4= timeit.Timer('so_q_join(1000)','from __main__ import so_q_join')
>>> t4.timeit(number=10000000)
9.4431309007150048
Не забывайте о им замену:
def so_question_uri_namedsub(q_num):
return "%(domain)s%(questions)s/%(q_num)d" % locals()
будьте осторожны с объединением строк в цикле! стоимость конкатенации строк пропорциональна длине результата. Петля ведет вас прямо в страну N-квадрат. Некоторые языки оптимизируют конкатенацию к последней выделенной строке, но рискованно рассчитывать на компилятор для оптимизации вашего квадратичного алгоритма до линейного. Лучше всего использовать примитив (join
?), который принимает весь список строк, выполняет одно выделение и конкатенирует все в один присест.
" поскольку конкатенация строк увидела большие повышения производительности..."
Если производительность имеет значение, это хорошо знать.
однако проблемы с производительностью, которые я видел, никогда не сводились к строковым операциям. У меня вообще возникли проблемы с вводом-выводом, сортировкой и O (n2) операции являются узкими местами.
пока строковые операции не станут ограничителями производительности, я буду придерживаться очевидных вещей. В основном, это подстановка, когда это одна строка или меньше, конкатенация, когда это имеет смысл, и инструмент шаблона (например, Мако), когда он большой.
то, что вы хотите объединить/интерполировать и как вы хотите отформатировать результат, должно управлять вашим решением.
интерполяция строк позволяет легко добавлять форматирование. Фактически, ваша версия интерполяции строк не делает то же самое, что и ваша версия конкатенации; она фактически добавляет дополнительную косую черту перед
Я просто тестировал скорость различных методов конкатенации/подстановки строк из любопытства. Поиск в google по этому вопросу привел меня сюда. Я думал, что опубликую результаты тестов в надежде, что это поможет кому-то решить.
import timeit
def percent_():
return "test %s, with number %s" % (1,2)
def format_():
return "test {}, with number {}".format(1,2)
def format2_():
return "test {1}, with number {0}".format(2,1)
def concat_():
return "test " + str(1) + ", with number " + str(2)
def dotimers(func_list):
# runs a single test for all functions in the list
for func in func_list:
tmr = timeit.Timer(func)
res = tmr.timeit()
print "test " + func.func_name + ": " + str(res)
def runtests(func_list, runs=5):
# runs multiple tests for all functions in the list
for i in range(runs):
print "----------- TEST #" + str(i + 1)
dotimers(func_list)
...После запуска runtests((percent_, format_, format2_, concat_), runs=5)
, я обнаружил, что метод % был примерно в два раза быстрее, чем другие на этих небольших строках. Метод concat всегда был самым медленным (почти). Были очень крошечные различия при переключении позиции в format()
метод, но переключение позиций всегда было по крайней мере .01 медленнее, чем метод обычного формата.
образец результатов теста:
test concat_() : 0.62 (0.61 to 0.63)
test format_() : 0.56 (consistently 0.56)
test format2_() : 0.58 (0.57 to 0.59)
test percent_() : 0.34 (0.33 to 0.35)
я запустил их, потому что я использую конкатенацию строк в своих сценариях, и мне было интересно, какова стоимость. Я запустил их в разных порядках, чтобы убедиться, что ничто не мешает, или получить лучшую производительность, будучи первым или последним. На боковой ноте я добавил несколько более длинных строковых генераторов в эти функции, такие как "%s" + ("a" * 1024)
и регулярный конкат был почти в 3 раза быстрее (1.1 против 2.8), чем использование format
и %
методы. Я думаю, это зависит от струн и того, чего вы пытаетесь достичь. Если производительность действительно имеет значение, может быть, лучше попробовать разные вещи и проверить их. Я склонен выбирать читаемость над скоростью, если скорость не становится проблемой, но это только я. Так что мне не понравилась моя копия / вставка, мне пришлось поставить 8 пробелов на все, чтобы это выглядело правильно. Я обычно использую 4.
помните, что стилистические решения are практические решения, если вы когда-либо планируете поддерживать или отлаживать свой код: -) есть знаменитая цитата из кнута (возможно, цитируя Хоара?): "Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация-это корень всего зла."
пока вы осторожны, чтобы (скажем) не превратить задачу O(n) в O(n2) задача, я бы пошел с тем, что вам легче всего понять..
Я использую замену везде, где могу. Я использую конкатенацию, только если я создаю строку, скажем, в for-loop.
на самом деле правильная вещь, в этом случае (построение путей) - использовать os.path.join
. Не конкатенация строк или интерполяция