Конкатенация строк противподстановка строк в Python

StackOverflow https://stackoverflow.com/questions/376461

Вопрос

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

Для конкретного примера, как следует обращаться с построением гибких URI:

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)

Редактировать:Также были предложения об объединении списка строк и об использовании именованной подстановки.Это варианты по центральной теме, которая заключается в том, какой способ является правильным способом сделать это в какое время?Спасибо за ответы!

Это было полезно?

Решение

Конкатенация происходит (значительно) быстрее в соответствии с моей машиной.Но со стилистической точки зрения я готов заплатить цену за замену, если производительность не критична.Ну, а если мне нужно форматирование, то нет необходимости даже задавать этот вопрос...нет другого выбора, кроме как использовать интерполяцию / шаблонирование.

>>> 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-squared.Некоторые языки оптимизируют конкатенацию до самой последней выделенной строки, но рискованно рассчитывать на то, что компилятор оптимизирует ваш квадратичный алгоритм вплоть до линейного.Лучше всего использовать примитив (join?), который принимает весь список строк, выполняет одно выделение и объединяет их все за один раз.

"Поскольку конкатенация строк значительно повысила производительность ..."

Если производительность имеет значение, это полезно знать.

Однако проблемы с производительностью, с которыми я сталкивался, никогда не сводились к строковым операциям.Обычно у меня возникали проблемы с вводом-выводом, сортировкой и выводом(n2) операции, являющиеся узкими местами.

До тех пор, пока строковые операции не станут ограничителями производительности, я буду придерживаться очевидных вещей.В основном, это подстановка, когда это одна строка или меньше, конкатенация, когда это имеет смысл, и инструмент шаблона (например, Mako), когда он большой.

То, что вы хотите объединить / интерполировать и как вы хотите отформатировать результат, должно определять ваше решение.

  • Интерполяция строк позволяет вам легко добавлять форматирование.Фактически, ваша версия интерполяции строк не выполняет то же самое, что ваша версия конкатенации;это фактически добавляет дополнительную косую черту перед q_num параметр.Чтобы сделать то же самое, вам пришлось бы написать return DOMAIN + QUESTIONS + "/" + str(q_num) в этом примере.

  • Интерполяция упрощает форматирование цифр; "%d of %d (%2.2f%%)" % (current, total, total/current) было бы гораздо менее читабельно в форме конкатенации.

  • Конкатенация полезна, когда у вас нет фиксированного количества элементов для упорядочивания.

Кроме того, знайте, что Python 2.6 вводит новую версию интерполяции строк, называемую создание шаблонов строк:

def so_question_uri_template(q_num):
    return "{domain}/{questions}/{num}".format(domain=DOMAIN,
                                               questions=QUESTIONS,
                                               num=q_num)

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

Я просто тестировал скорость различных методов конкатенации / подстановки строк из любопытства.Поиск в 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() метод, но переключение позиций всегда было как минимум на 0,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) и обычный concat был почти в 3 раза быстрее (1.1 против 2.8), чем при использовании format и % методы.Я думаю, это зависит от строк и того, чего вы пытаетесь достичь.Если производительность действительно имеет значение, возможно, было бы лучше попробовать разные вещи и протестировать их.Я склонен предпочитать удобочитаемость скорости, если только скорость не становится проблемой, но это касается только меня.Так что мне не понравилось мое копирование / вставка, мне пришлось поставить 8 пробелов на все, чтобы все выглядело правильно.Обычно я использую 4.

Помните, стилистические решения являются практические решения, если вы когда-нибудь планируете поддерживать или отлаживать свой код :-) Есть известная цитата Кнута (возможно, цитирующая Хоара?):"Мы должны забыть о небольшой эффективности, скажем, примерно в 97% случаев:преждевременная оптимизация - это корень всего зла".

До тех пор, пока вы будете осторожны, чтобы (скажем) не превратить O (n) задачу в O (n2) задание, я бы выбрал то, которое вам покажется наиболее простым для понимания..

Я использую замену везде, где только могу.Я использую конкатенацию только в том случае, если я создаю строку, скажем, в цикле for.

На самом деле правильным решением в данном случае (построение путей) является использование os.path.join.Не конкатенация строк или интерполяция

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top