Форматирование строки:% против .format - PullRequest
1292 голосов
/ 22 февраля 2011

В Python 2.6 введен метод str.format() с немного отличающимся синтаксисом от существующего оператора %.Что лучше и для каких ситуаций?

  1. Следующее использует каждый метод и имеет одинаковый результат, так в чем же разница?

    #!/usr/bin/python
    sub1 = "python string!"
    sub2 = "an arg"
    
    a = "i am a %s" % sub1
    b = "i am a {0}".format(sub1)
    
    c = "with %(kwarg)s!" % {'kwarg':sub2}
    d = "with {kwarg}!".format(kwarg=sub2)
    
    print a    # "i am a python string!"
    print b    # "i am a python string!"
    print c    # "with an arg!"
    print d    # "with an arg!"
    
  2. Кроме того, когда происходит форматирование строки в Python?Например, если мой уровень ведения журнала установлен на ВЫСОКИЙ, я все равно получу удар для выполнения следующей операции %?И если да, есть ли способ избежать этого?

    log.debug("some debug info: %s" % some_info)
    

Ответы [ 16 ]

920 голосов
/ 22 февраля 2011

Чтобы ответить на ваш первый вопрос ... .format просто кажется более изощренным во многих отношениях. Раздражает % также то, как он может принимать переменную или кортеж. Можно подумать, что всегда будет работать следующее:

"hi there %s" % name

пока, если name окажется (1, 2, 3), будет выброшено TypeError. Чтобы гарантировать, что он всегда печатает, вам нужно сделать

"hi there %s" % (name,)   # supply the single argument as a single-item tuple

что просто ужасно. .format не имеет этих проблем. Также во втором примере, который вы привели, пример .format выглядит намного чище.

Почему бы вам не использовать его?

  • не зная об этом (я, прежде чем читать это)
  • должен быть совместим с Python 2.5

Чтобы ответить на ваш второй вопрос, форматирование строки происходит одновременно с любой другой операцией - когда вычисляется выражение форматирования строки. И Python, не будучи ленивым языком, вычисляет выражения перед вызовом функций, поэтому в вашем примере log.debug выражение "some debug info: %s"%some_info сначала будет вычислять, например, "some debug info: roflcopters are active", тогда эта строка будет передана в log.debug().

299 голосов
/ 14 июня 2011

То, что оператор по модулю (%) не может сделать, afaik:

tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)

результат

12 22222 45 22222 103 22222 6 22222

Очень полезно.

Другой момент: format(), будучи функцией, может использоваться в качестве аргумента в других функциях:

li = [12,45,78,784,2,69,1254,4785,984]
print map('the number is {}'.format,li)   

print

from datetime import datetime,timedelta

once_upon_a_time = datetime(2010, 7, 1, 12, 0, 0)
delta = timedelta(days=13, hours=8,  minutes=20)

gen =(once_upon_a_time +x*delta for x in xrange(20))

print '\n'.join(map('{:%Y-%m-%d %H:%M:%S}'.format, gen))

Результаты:

['the number is 12', 'the number is 45', 'the number is 78', 'the number is 784', 'the number is 2', 'the number is 69', 'the number is 1254', 'the number is 4785', 'the number is 984']

2010-07-01 12:00:00
2010-07-14 20:20:00
2010-07-28 04:40:00
2010-08-10 13:00:00
2010-08-23 21:20:00
2010-09-06 05:40:00
2010-09-19 14:00:00
2010-10-02 22:20:00
2010-10-16 06:40:00
2010-10-29 15:00:00
2010-11-11 23:20:00
2010-11-25 07:40:00
2010-12-08 16:00:00
2010-12-22 00:20:00
2011-01-04 08:40:00
2011-01-17 17:00:00
2011-01-31 01:20:00
2011-02-13 09:40:00
2011-02-26 18:00:00
2011-03-12 02:20:00
139 голосов
/ 22 февраля 2011

Предполагая, что вы используете модуль logging Python, вы можете передавать аргументы форматирования строки в качестве аргументов методу .debug(), а не выполнять форматирование самостоятельно:

log.debug("some debug info: %s", some_info)

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

110 голосов
/ 15 апреля 2016

Начиная с Python 3.6 (2016), вы можете использовать f-строки для замены переменных:

>>> origin = "London"
>>> destination = "Paris"
>>> f"from {origin} to {destination}"
'from London to Paris'

Обратите внимание на префикс f". Если вы попробуете это в Python 3.5 или более ранней версии, вы получите SyntaxError.

См. https://docs.python.org/3.6/reference/lexical_analysis.html#f-strings

57 голосов
/ 01 августа 2011

PEP 3101 предлагает заменить оператор % новым расширенным форматированием строк в Python 3, где это будет значение по умолчанию.

52 голосов
/ 03 сентября 2012

Но, пожалуйста, будьте осторожны, только сейчас я обнаружил одну проблему при попытке заменить все % на .format в существующем коде: '{}'.format(unicode_string) попытается закодировать unicode_string и, возможно, потерпит неудачу.

Просто посмотрите на этот интерактивный журнал сеансов Python:

Python 2.7.2 (default, Aug 27 2012, 19:52:55) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
; s='й'
; u=u'й'
; s
'\xd0\xb9'
; u
u'\u0439'

s - это просто строка (называемая 'байтовым массивом' в Python3), а u - это строка Unicode (называется «строка» в Python3):

; '%s' % s
'\xd0\xb9'
; '%s' % u
u'\u0439'

Когда вы передаете объект Unicode в качестве параметра для оператора %, он будет генерировать строку Unicode, даже если исходная строка не была Unicode:

; '{}'.format(s)
'\xd0\xb9'
; '{}'.format(u)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character u'\u0439' in position 0: ordinal not in range(256)

, но функция .format вызовет UnicodeEncodeError:

; u'{}'.format(s)
u'\xd0\xb9'
; u'{}'.format(u)
u'\u0439'

и будет работать с аргументом Unicode нормально, только если исходная строка была Unicode.

; '{}'.format(u'i')
'i'

или если строка аргумента может быть преобразована в строку (так называемый «байтовый массив»)

34 голосов
/ 04 декабря 2014

Еще одно преимущество .format (которого я не вижу в ответах): оно может принимать свойства объекта.

In [12]: class A(object):
   ....:     def __init__(self, x, y):
   ....:         self.x = x
   ....:         self.y = y
   ....:         

In [13]: a = A(2,3)

In [14]: 'x is {0.x}, y is {0.y}'.format(a)
Out[14]: 'x is 2, y is 3'

Или в качестве аргумента ключевого слова:

In [15]: 'x is {a.x}, y is {a.y}'.format(a=a)
Out[15]: 'x is 2, y is 3'

Насколько я могу судить, это невозможно с %.

30 голосов
/ 13 мая 2014

Как я обнаружил сегодня, старый способ форматирования строк с помощью % не поддерживает Decimal, модуль Python для десятичной фиксированной точки и арифметики с плавающей точкой, из коробки.

Пример (с использованием Python 3.3.5):

#!/usr/bin/env python3

from decimal import *

getcontext().prec = 50
d = Decimal('3.12375239e-24') # no magic number, I rather produced it by banging my head on my keyboard

print('%.50f' % d)
print('{0:.50f}'.format(d))

Выход:

0.00000000000000000000000312375239000000009907464850 0.00000000000000000000000312375239000000000000000000

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

28 голосов
/ 13 июня 2011

% дает лучшую производительность, чем format из моего теста.

Код теста:

Python 2.7.2:

import timeit
print 'format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')")
print '%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')")

Результат:

> format: 0.470329046249
> %: 0.357107877731

Python 3.5.2

import timeit
print('format:', timeit.timeit("'{}{}{}'.format(1, 1.23, 'hello')"))
print('%:', timeit.timeit("'%s%s%s' % (1, 1.23, 'hello')"))

Результат

> format: 0.5864730989560485
> %: 0.013593495357781649

Это выглядит в Python2, разница невелика, тогда как в Python3, % намного быстрее, чемformat.

Спасибо @Chris Cogdon за образец кода.

Редактировать:

Повторное тестирование в Python 3.7.2 в июле 2019 года.

Результат:

> format: 0.86600608
> %: 0.630180146

Нет большой разницы.Я думаю, Python постепенно улучшается.

14 голосов
/ 21 августа 2014

Как примечание: вам не нужно наносить удар по производительности, чтобы использовать новый стиль форматирования с ведением журнала. Вы можете передать любой объект logging.debug, logging.info и т. Д., Который реализует магический метод __str__. Когда модуль протоколирования решил, что он должен выдать ваш объект сообщения (каким бы он ни был), перед этим он вызывает str(message_object). Таким образом, вы можете сделать что-то вроде этого:

import logging


class NewStyleLogMessage(object):
    def __init__(self, message, *args, **kwargs):
        self.message = message
        self.args = args
        self.kwargs = kwargs

    def __str__(self):
        args = (i() if callable(i) else i for i in self.args)
        kwargs = dict((k, v() if callable(v) else v) for k, v in self.kwargs.items())

        return self.message.format(*args, **kwargs)

N = NewStyleLogMessage

# Neither one of these messages are formatted (or calculated) until they're
# needed

# Emits "Lazily formatted log entry: 123 foo" in log
logging.debug(N('Lazily formatted log entry: {0} {keyword}', 123, keyword='foo'))


def expensive_func():
    # Do something that takes a long time...
    return 'foo'

# Emits "Expensive log entry: foo" in log
logging.debug(N('Expensive log entry: {keyword}', keyword=expensive_func))

Все это описано в документации по Python 3 (https://docs.python.org/3/howto/logging-cookbook.html#formatting-styles). Однако, это будет работать и с Python 2.6 (https://docs.python.org/2.6/library/logging.html#using-arbitrary-objects-as-messages).

Одним из преимуществ использования этого метода, помимо того факта, что он не зависит от стиля форматирования, является то, что он допускает ленивые значения, например, функция expensive_func выше. Это обеспечивает более элегантную альтернативу советам, которые даются в документации по Python: https://docs.python.org/2.6/library/logging.html#optimization.

...