Параметры форматирования строк: плюсы и минусы

Это два очень популярных способа форматирования строки в Python. Один использует dict:

>>> 'I will be %(years)i on %(month)s %(day)i' % {'years': 21, 'month': 'January', 'day': 23}
'I will be 21 on January 23'

И другой, используя простой tuple:

>>> 'I will be %i on %s %i' % (21, 'January', 23)
'I will be 21 on January 23'

Первый способ более читабельен, но второй быстрее писать. Я фактически использую их невнятно.

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

Ответ 1

Почему format() более гибкий, чем % строковые операции

Я думаю, вы действительно должны придерживаться метода format() str, потому что это предпочтительный способ форматирования строк и, вероятно, заменит операцию форматирования строк в будущем.

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

>>> string = 'I will be {} years and {} months on {month} {day}'
>>> some_date = {'month': 'January', 'day': '1st'}
>>> diff = [3, 11] # years, months
>>> string.format(*diff, **some_date)
'I will be 3 years and 11 months on January 1st'

будет работать и следующее:

>>> string = 'On {month} {day} it will be {1} months, {0} years'
>>> string.format(*diff, **some_date)
'On January 1st it will be 11 months, 3 years'

Есть еще одна причина в пользу format(). Поскольку это метод, он может быть передан как обратный вызов, как в следующем примере:

>>> data = [(1, 2), ('a', 'b'), (5, 'ABC')]
>>> formatter = 'First is "{0[0]}", then comes "{0[1]}"'.format
>>> for item in map(formatter, data):
    print item


First is "1", then comes "2"
First is "a", then comes "b"
First is "5", then comes "ABC"

Разве это не намного гибче, чем операция форматирования строк?

См. дополнительные примеры на странице документации для сравнения между операциями % и .format().

Сравнение строкового форматирования строк % со словарным

Как правило, существуют три способа вызова % строковых операций (да, три, а не два):

base_string % values

и они отличаются типом values (что является следствием того, что является содержимым base_string):

  • это может быть tuple, затем они заменяются один за другим, в порядке их появления в кортеже,

    >>> 'Three first values are: %f, %f and %f' % (3.14, 2.71, 1)
    'Three first values are: 3.140000, 2.710000 and 1.000000'
    
  • это может быть dict (словарь), затем они заменяются на основе ключевых слов,

    >>> 'My name is %(name)s, I am %(age)s years old' % {'name':'John','age':98}
    'My name is John, I am 98 years old'
    
  • это может быть одно значение, если base_string содержит одно место, где должно быть вставлено значение:

    >>> 'This is a string: %s' % 'abc'
    'This is a string: abc'
    

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

Но есть что-то, что специфично только для операции форматирования строк на основе словаря и довольно недоступно в остальных трех типах операций форматирования. Это способность с легкостью заменить спецификаторы фактическими именами переменных:

>>> name = 'John'
>>> surname = 'Smith'
>>> age = 87
# some code goes here
>>> 'My name is %(surname)s, %(name)s %(surname)s. I am %(age)i.' % locals()
'My name is Smith, John Smith. I am 87.'

Только для записи: конечно, вышеупомянутое можно легко заменить, используя format(), распакуя словарь следующим образом:

>>> 'My name is {surname}, {name} {surname}. I am {age}.'.format(**locals())
'My name is Smith, John Smith. I am 87.'

Есть ли у кого-нибудь еще идея, что может быть особенностью, характерной для одного типа операции форматирования строк, но не с другим? Было бы очень интересно услышать об этом.

Ответ 2

Я не совсем отвечаю на ваш вопрос, но просто подумал, что было бы неплохо бросить format в ваш микс.

Я лично предпочитаю синтаксис format для обоих:

'I will be {years} on {month} {day}'.format(years=19, month='January', day=23)

Если я хочу что-то компактное, я просто пишу:

'I will be {} on {} {}'.format(19, 'January', 23)

И format отлично играет с объектами:

class Birthday:
  def __init__(self, age, month, day):
    self.age = age
    self.month = month
    self.day = day

print 'I will be {b.age} on {b.month} {b.day}'.format(b = Birthday(19, 'January', 23))

Ответ 3

Я не отвечаю на вопрос, но просто объясняю идею, которую я придумал в TIScript.

Я ввел так называемые функции "stringizer": любая функция с именем, начинающимся с "$", является строкатором. Компилятор рассматривает '$ name (' и ')' как цитаты строкового литерала в сочетании с вызовом функции.

Пример:

$print(I will be {b.age} on {b.month} {b.day});

фактически скомпилирован в

$print("I will be ", b.age, " on ",b.month," ",b.day);

где даже аргументы всегда являются буквальными строками, а нечетные - выражениями. Таким образом, можно определить пользовательские стройзеры, которые используют различную обработку форматирования/аргумента.

Например, Element.$html(Hello <b>{who}</b>); будет применять вытеснение HTML в выражениях. И этот Element.$(option[value={12}]); будет делать выбор в стиле jQuery.

Довольно удобно и гибко.

Я не уверен, что можно сделать что-то подобное в Python без изменения его компилятора. Подумайте, как идея.