Условно оцененные отладочные операторы в Python - PullRequest
11 голосов
/ 03 октября 2011

В Python есть несколько способов печати «трассировки» вывода.print, import logging, stdout.write можно использовать для печати отладочной информации, но все они имеют один недостаток: даже если пороговое значение регистратора слишком велико или поток закрыт, Python по-прежнему будет оценивать аргументы оператора print,( Строгая оценка ) Это может стоить строкового формата или более.

Очевидное решение - поместить код, создающий строку, в лямбду и использовать нашу собственную функцию регистрации для вызова лямбдаусловно (проверяется встроенная переменная __debug__, которая устанавливается в False всякий раз, когда python запускается с -O для оптимизации):

def debug(f):
  if __debug__:
    print f()
    #stdout.write(f())
    #logging.debug(f())

for currentItem in allItems:
  debug(lambda:"Working on {0}".format(currentItem))

Преимущество заключается не в вызовах str(currentItem) и string.formatв сборках релизов, и недостатком является необходимость вводить lambda: в каждом операторе журналирования.

Оператор Python * assert специально обрабатывается компилятором Python.Если python запускается с -O, то любые операторы assert отбрасываются без какой-либо оценки.Вы можете использовать это для создания другого условно оцененного оператора ведения журнала:

assert(logging.debug("Working on {0}".format(currentItem)) or True)

Эта строка не будет оцениваться, когда Python запускается с -O.

Операторы короткого замыкания 'и'и' или 'можно даже использовать:

__debug__ and logging.debug("Working on {0}".format(currentItem));

Но теперь у нас до 28 символов плюс код для выходной строки.

Вопрос, который я получаю:Существуют ли какие-либо стандартные операторы Python или функции, которые имеют те же свойства условной оценки, что и оператор assert?Или у кого-нибудь есть альтернативы методам, представленным здесь?

Ответы [ 4 ]

3 голосов
/ 03 октября 2011

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

debug(lambda:"Working on {0}".format(currentItem))

становится

debug("Working on {0}", currentItem)

и

if __debug__:
    def debug(format, *values):
        print format.format(*values)
else:
    def debug(format, *values): pass

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

2 голосов
/ 03 октября 2011

Интересно, насколько вызов logging.debug влияет на производительность, когда нет обработчиков.

Однако оператор if __debug__: вычисляется только один раз, даже в теле функции

$ python -O
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> import logging
>>> def debug(*a, **kw):
...  if __debug__:
...   logging.debug(*a, **kw)
... 
>>> dis.dis(debug)
  2           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE        
>>> 

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

FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = { 'clientip' : '192.168.0.1', 'user' : 'fbloggs' }
debug('Protocol problem: %s', 'connection reset', extra=d)

В этом случае строка сообщения никогда не оценивается, если оптимизация отключена.

0 голосов
/ 03 октября 2011

Вы можете использовать метод eval :

import inspect

def debug(source):
  if __debug__:
    callers_locals = inspect.currentframe().f_back.f_locals
    print eval(source, globals(),  callers_locals)

for currentItem in ('a', 'b'):
  debug('"Working on {0}".format(currentItem)')
0 голосов
/ 03 октября 2011

любые стандартные операторы Python или функции с тем же условным поведением, что и assert -> no, насколько я знаю.

Обратите внимание, что logging функции не выполняют интерполяцию строк, если порог слишком высок (но вы все равно платите за вызов метода и некоторые проверки внутри).

Вы можете расширить предложение Дана Д., запустив monkeypatching logging.logger при запуске кода:

import logging
if __debug__:
    for methname in ('debug', 'info', 'warning', 'error', 'exception'):
         logging.logger.setattr(methname, lambda self, *a, **kwa: None)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...