2010-01-05 1 views
9

мой код Python переплетен с большим количеством вызовов функций, используемых для отладки (| профилирования | трассировку и т.д.) , например:питон эквивалент «#define FUNC()» или как закомментировать вызов функции в 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') 

Я хочу, чтобы #define эти ресурсоемкие функции из кода. что-то вроде с эквивалентным

#define logging.debug(val) 

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

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

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

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

ответ

17

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

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

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

if __debug__: doStuff() 

и

if 0: doStuff() 

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

if False: doStuff() 

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

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

Что мне кажется, недостаток в языке - мы надеемся, это фиксируется в Python 3.

Edit:

Это исправлено в Python 3: Назначение True или False now gives a SyntaxError. Поскольку истина и ложь Константа в Python 3, это означает, что if False: doStuff() теперь оптимизировано:

>>> def f(): 
...  if False: print("illogical") 
... 
>>> dis.dis(f) 
    2   0 LOAD_CONST    0 (None) 
       3 RETURN_VALUE   
+0

Он работает так же в Python 3. – Brian

+0

, если 0: | если __debug__: | если переменная: + оптимизация, будь то через скрипт или хороший редактор, может быть лучшей идеей (лучше, чем prepending #, которая не обрабатывает многострочные строки).возможно, python не идеален после всех –

+1

@Dave: хороший вирус python вводит в код строку «False, True = True, False». замечательные вещи могут случиться –

0

Используйте модуль области видимости?

from config_module import debug_flag

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

+0

так, я бы добавить проверку перед каждым вызовом функции? –

+0

@random guy: Dirtier, чем это уже со всеми этими отладчиками? – cschol

+0

@jldupont: не нужно ли мне менять все вызовы на вход в мой класс-оболочку? –

0

Я думаю, что полностью исключить вызов функции не представляется возможным, поскольку 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() 

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

-1

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

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

logging.root.setLevel(logging.CRITICAL) 
+1

i havent ясно дал понять. Я ищу взломать python общего назначения, чтобы «прокомментировать» вызовы функций. то есть, я хочу, чтобы «прокомментированные» функции даже не вызывались. ваше предложение не отменяет вызов функции logging.debug(). функция вызывается и выполняет некоторую обработку. это то, чего я хочу избежать –

+0

Вы поняли, но я не думаю, что это pythonic делать то, о чем вы просили. – user238424

0

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

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

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

+0

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

1

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

+0

Существует также http://code.google.com/p/pypreprocessor/. –

2

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

Единственным возможным решением, кроме preprocessor suggestion, является использование некоторых bytecode hacking. Я даже не начну представлять, как это должно работать с точки зрения 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 цели.

Это одна функция более традиционный 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.

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

0

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

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

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

logging.debug = nuzzing 
+0

мой оригинальный вопрос включает в себя вариант этого решения –

+0

Да, но, позволяя вашей функции-заполнителя вызывать себя рекурсивно, вы оставляете дверь открытой для немедленной неудачи (превышен стек глубины превышения). Недавний компилятор cpython достаточно умен, чтобы уменьшить любую функцию, которая в любом случае перейдет в noop, поэтому любые вызовы к ней эффективно комментируются. Я не думаю, что вы найдете лучшее решение. – richo

0

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^