Конкатенация строк и замена строк в Python

В 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)

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

Ответ 1

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

>>> 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

Ответ 2

Не забывайте о названной замене:

def so_question_uri_namedsub(q_num):
    return "%(domain)s%(questions)s/%(q_num)d" % locals()

Ответ 3

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

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

Однако проблемы с производительностью, которые я видел, никогда не приходили в строковые операции. Я, как правило, столкнулся с проблемами ввода/вывода, сортировки и операций O (n 2), являющихся узкими местами.

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

Ответ 4

Будьте осторожны с конкатенацией строк в цикле! Стоимость конкатенации строк пропорциональна длине результата. Цикл ведет вас прямо к земле N-квадратов. Некоторые языки оптимизируют конкатенацию к самой последней выделенной строке, но рискованно рассчитывать на компилятор для оптимизации вашего квадратичного алгоритма до линейного. Лучше всего использовать примитив (join?), Который принимает весь список строк, делает одно выделение и объединяет их всех за один раз.

Ответ 5

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

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

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

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

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

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

Строка templating намечена, чтобы в конечном итоге заменить% -interpolation, но это не произойдет довольно долго, я думаю.

Ответ 6

Я просто тестировал скорость различных методов конкатенации/замены строк из любопытства. Поиск 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.

Ответ 7

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

Пока вы не станете (скажем) превращать задачу O (n) в задачу O (n 2), я бы пошел в зависимости от того, что вам будет легче всего понять.

Ответ 8

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

Ответ 9

Собственно, правильная вещь в этом случае (пути построения) заключается в использовании os.path.join. Не конкатенация или интерполяция строк