Python-эквивалент '#define func ()' или как закомментировать вызов функции в Python - PullRequest
9 голосов
/ 05 января 2010

мой код на python чередуется с большим количеством вызовов функций, используемых для (отладка | профилирование | трассировка и т. Д.) например:

import logging

logging.root.setLevel(logging.DEBUG)
logging.debug('hello')
j = 0
for i in range(10):
    j += i
    logging.debug('i %d j %d' % (i,j))
print(j)
logging.debug('bye')

Я хочу # определить эти ресурсоемкие функции из кода. что-то вроде эквивалента C

#define logging.debug(val)

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

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

def lazy(*args): pass
logging.debug = lazy

вышеупомянутая идея все еще вызывает функцию и может создать множество других проблем

Ответы [ 9 ]

17 голосов
/ 05 января 2010

В Python нет препроцессора, хотя вы можете запустить исходный код Python через внешний препроцессор, чтобы получить тот же эффект - например, sed "/logging.debug/d" удалит все команды ведения журнала отладки. Это не очень элегантно - вам понадобится какая-то система сборки для запуска всех ваших модулей через препроцессор и, возможно, для создания нового дерева каталогов обработанных файлов .py перед запуском основного скрипта.

В качестве альтернативы, если вы поместите все ваши отладочные операторы в блок if __debug__:, они будут оптимизированы при запуске python с флагом -O (optimize).

Кроме того, я проверил код с модулем dis, чтобы убедиться, что он оптимизирован. Я обнаружил, что оба

if __debug__: doStuff()

и

if 0: doStuff()

оптимизированы, но

if False: doStuff()

нет. Это потому, что False является обычным объектом Python, и вы можете сделать это:

>>> False = True
>>> if False: print "Illogical, captain"
Illogical, captain

Что мне кажется недостатком в языке - надеюсь, это исправлено в Python 3.

Edit:

Это исправлено в Python 3: присвоение True или False теперь дает SyntaxError . Поскольку True и False являются константами в Python 3, это означает, что if False: doStuff() теперь оптимизировано:

>>> def f():
...     if False: print( "illogical")
... 
>>> dis.dis(f)
  2           0 LOAD_CONST               0 (None) 
              3 RETURN_VALUE         
2 голосов
/ 05 января 2010

Хотя я думаю, что вопрос совершенно ясен и действителен (несмотря на многочисленные ответы, которые говорят об обратном), краткий ответ: «В Python для этого нет поддержки».

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

Например, посмотрите на следующие две функции:

>>> def func():
...    if debug:  # analogous to if __debug__:
...       foo
>>> dis.dis(func)
  2           0 LOAD_GLOBAL              0 (debug)
              3 JUMP_IF_FALSE            8 (to 14)
              6 POP_TOP

  3           7 LOAD_GLOBAL              1 (foo)
             10 POP_TOP
             11 JUMP_FORWARD             1 (to 15)
        >>   14 POP_TOP
        >>   15 LOAD_CONST               0 (None)
             18 RETURN_VALUE

Здесь вы можете отсканировать LOAD_GLOBAL из debug и устранить его и все, вплоть до цели JUMP_IF_FALSE.

Это более традиционная функция debug () в стиле C, которая хорошо стирается препроцессором:

>>> def func2():
...    debug('bar', baz)
>>> dis.dis(func2)
  2           0 LOAD_GLOBAL              0 (debug)
              3 LOAD_CONST               1 ('bar')
              6 LOAD_GLOBAL              1 (baz)
              9 CALL_FUNCTION            2
             12 POP_TOP
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

Здесь вы должны найти LOAD_GLOBAL из debug и стереть все до соответствующего CALL_FUNCTION.

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

1 голос
/ 05 января 2010

Ну, вы всегда можете реализовать свой собственный простой препроцессор, который делает свое дело. Или, что еще лучше, вы можете использовать уже существующий. Скажи http://code.google.com/p/preprocess/

0 голосов
/ 08 февраля 2010

Мне нравится решение 'if __debug_', за исключением того, что размещение его перед каждым вызовом немного отвлекает и уродливо. У меня была такая же проблема, и я преодолел ее, написав скрипт, который автоматически анализирует ваши исходные файлы и заменяет операторы журналирования на операторы прохода (и закомментированные копии операторов журналирования). Это также может отменить это преобразование.

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

Вы можете найти скрипт здесь: http://dound.com/2010/02/python-logging-performance/

0 голосов
/ 06 января 2010

определяет функцию, которая ничего не делает, т.е.

def nuzzing(*args, **kwargs): pass

Затем просто перегрузите все функции, от которых вы хотите избавиться, своей функцией, ала

logging.debug = nuzzing
0 голосов
/ 05 января 2010

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

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

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

0 голосов
/ 05 января 2010

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

Но, безусловно, самый чистый подход - игнорировать сообщения журнала с низким приоритетом (как вы предложили):

logging.root.setLevel(logging.CRITICAL)
0 голосов
/ 05 января 2010

Я думаю, что полностью отказаться от вызова функции невозможно, поскольку Python работает иначе, чем C. #define происходит в прекомпиляторе до компиляции кода. В Python такого нет.

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

Примерно так:

Файл logging.py

#Main module
def log():
    print 'logging'

def main():
    log()
    print 'Hello'
    log()

Файл call_log.py

import re
#To log or not to log, that's the question
log = True

#Change the loging
with open('logging.py') as f:
    new_data = []
    for line in f:
       if not log and re.match(r'\s*log.*', line):
         #Comment
         line = '#' + line
       if log and re.match(r'#\s*log.*', line):
         #Uncomment
         line = line[1:]
       new_data.append(line)

#Save file with adequate log level
with open('logging.py', 'w') as f:
   f.write(''.join(new_data))


#Call the module
import logging
logging.main()

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

0 голосов
/ 05 января 2010

Использовать переменную области модуля?

from config_module import debug_flag

и используйте эту «переменную» для доступа к функциям журналирования. Вы должны создать себе модуль logging, который использует debug_flag для управления функциями ведения журнала.

...