Идиоматическое ведение журнала Python: форматирование строки + список аргументов против форматирования строки строки – что предпочтительнее?

Полезно ли вызывать функции ведения журнала с помощью строки формата + список аргументов против форматирования inline?

Я видел (и написал) код регистрации, который использует форматирование строки inline:

logging.warn("%s %s %s" % (arg1, arg2, arg3)) 

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

 logging.warn("%s %s %s", arg1, arg2, arg3) 

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

Я на правильном пути здесь, или я что-то пропустил?

    3 Solutions collect form web for “Идиоматическое ведение журнала Python: форматирование строки + список аргументов против форматирования строки строки – что предпочтительнее?”

    IMHO, для сообщений, которые, скорее всего, будут отображаться, например, те, которые приводятся к error или warn этом, не имеют большого значения.

    Для сообщений, которые менее вероятно отображаются, я бы определенно пошел на вторую версию, главным образом по соображениям производительности. Я часто даю большие объекты в качестве параметра для info , которые реализуют дорогостоящий метод __str__ . Понятно, что отправка этой предварительно отформатированной info была бы отбросом производительности.

    ОБНОВИТЬ

    Я просто проверил исходный код модуля logging и, действительно, форматирование выполняется после проверки уровня журнала. Например:

     class Logger(Filterer): # snip def debug(self, msg, *args, **kwargs): # snip if self.isenabledfor(debug): self._log(debug, msg, args, **kwargs) 

    Можно заметить, что msg и args не затронуты между log вызовов и проверкой уровня журнала.

    ОБНОВЛЕНИЕ 2

    Spired by Levon, позвольте мне добавить несколько тестов для объектов с дорогостоящим методом __str__ :

     $ python -m timeit -n 1000000 -s "import logging" -s "logger = logging.getLogger('foo')" -s "logger.setLevel(logging.ERROR)" "logger.warn('%s', range(0,100))" 1000000 loops, best of 3: 1.52 usec per loop $ python -m timeit -n 1000000 -s "import logging" -s "logger = logging.getLogger('foo')" -s "logger.setLevel(logging.ERROR)" "logger.warn('%s' % range(0,100))" 1000000 loops, best of 3: 10.4 usec per loop 

    На практике это может привести к значительному повышению производительности.

    В случае, если это полезно, вот быстрый тест времени для двух вариантов форматирования:

     In [61]: arg1='hello' In [62]: arg2='this' In [63]: arg3='is a test' In [70]: timeit -n 10000000 "%s %s %s" % (arg1, arg2, arg3) 10000000 loops, best of 3: 284 ns per loop In [71]: timeit -n 10000000 "%s %s %s", arg1, arg2, arg3 10000000 loops, best of 3: 119 ns per loop 

    кажется, дает второй подход к краю.

    Избегание форматирования строки в строке позволяет сэкономить некоторое время, если текущий уровень ведения журнала фильтрует сообщение журнала (как я и ожидал) – но не так много:

     In [1]: import logging In [2]: logger = logging.getLogger('foo') In [3]: logger.setLevel(logging.ERROR) In [4]: %timeit -n 1000000 logger.warn('%s %s %s' % ('a', 'b', 'c')) 1000000 loops, best of 3: 1.09 us per loop In [12]: %timeit -n 1000000 logger.warn('%s %s %s', 'a', 'b', 'c') 1000000 loops, best of 3: 946 ns per loop 

    Таким образом, как указал user1202136 , общая разница в производительности зависит от того, сколько времени потребуется для форматирования строки (что может быть значительным в зависимости от стоимости вызова __str__ при передаче аргументов функции регистрации).

    Python - лучший язык программирования в мире.