2010-01-05 22 views
9

我的Python代碼被交錯,有很多的函數調用用於(調試|分析|跟蹤等) 例如:蟒蛇相當於「#定義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') 

我想#定義這些資源消耗功能的代碼。類似的C相當於

#define logging.debug(val) 

是的,我知道日誌模塊日誌記錄級別的機制可以用來屏蔽掉低於設定日誌級別測井公司。但是,即時通訊要求有Python解釋器跳過功能的一般方式(即需要時間,即使他們不這樣做太多運行)

一個想法是重新定義我要評論伸到空函數功能:

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

上述想法還調用一個函數,並可能產生的其他問題

回答

17

Python沒有預處理器,儘管您可以通過外部預處理器運行您的python源代碼以獲得相同的效果 - 例如, sed "/logging.debug/d"將刪除所有調試日誌記錄命令。但這並不是很優雅 - 您最終需要某種構建系統來通過預處理器運行所有模塊,並可能在運行主腳本之前創建處理後的.py文件的新目錄樹。

或者,如果你把所有的調試語句放在一個if __debug__:模塊中,當python運行-O(優化)標誌時,它們會被優化。

另外,我用dis模塊檢查了代碼,以確保它確實得到了優化。我發現,這兩個

if __debug__: doStuff() 

if 0: doStuff() 

進行了優化,但

if False: doStuff() 

不是。這是因爲假是一個普通的Python對象,你實際上可以做到這一點:

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

這似乎是我的語言缺陷 - 希望它是固定在Python 3

編輯:

這在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 2010-01-05 14:44:16

+0

如果0:如果__debug__:|如果是變量:+優化,無論是通過一個腳本還是一個好的編輯器都可能是最好的想法(比預處理#不處理多行語句更好)。也許python並不是完美的 – 2010-01-05 14:44:48

+1

@Dave:一個很好的python病毒會在代碼中注入'False,True = True,False'行。美妙的事情可能發生 – 2010-01-05 14:55:40

0

無數使用模塊範圍的變量?

from config_module import debug_flag

,並使用該「可變的」,以柵極訪問日誌功能(一個或多個)。您將自己構建一個logging模塊,該模塊使用debug_flag來關閉日誌記錄功能。

+0

,所以我必須在每個函數調用之前添加支票? – 2010-01-05 13:15:47

+0

@random人:比它已經是所有這些調試語句Dirtier? – cschol 2010-01-05 13:25:23

+0

@jldupont:我不得不改變所有的電話記錄到我的包裝類? – 2010-01-05 13:29:26

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

我沒有讓自己清楚。我正在尋找一個通用的python hack來'註釋掉'函數調用。也就是說,我希望「評論」功能不被稱爲。您的建議不會消除對logging.debug()的函數調用。該函數被調用,並進行一些處理。這是我想要避免的 – 2010-01-05 14:59:15

+0

你已經明確表態了,但我不認爲這是你所要求的pythonic。 – user238424 2010-01-05 15:03:11

0

你這樣做之前,你有沒有異型驗證記錄實際上正在大量時間?您可能會發現,您花更多時間嘗試刪除電話,而不是您節省的時間。

接下來,你有沒有嘗試類似Psyco?如果你已經設置了一些東西來禁止日誌記錄,那麼Psyco可能會優化掉大部分調用日誌記錄功能的開銷,注意到它總是會在沒有任何操作的情況下返回。

如果您仍然發現日誌記錄花費了相當可觀的時間,那麼您可能希望查看是否覆蓋重要循環內的日誌記錄功能,可能通過將本地變量綁定到日誌記錄功能或適當的虛擬功能(或在調用它之前檢查None)。

+0

我沒有說清楚。我以日誌記錄模塊和性能爲例。還有其他原因需要評論功能,例如,從代碼中刪除功能 – 2010-01-05 15:03:23

1

那麼,你總是可以實現你自己的簡單的預處理器,做到這一點。或者,更好的是,您可以使用現有的一個。說http://code.google.com/p/preprocess/

+0

還有http://code.google.com/p/pypreprocessor/。 – 2011-03-16 03:41:41

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 

在這裏,你可以掃描的debugLOAD_GLOBAL,並消除它,一切都到JUMP_IF_FALSE目標。

這一個是得到很好的預處理器抹殺更傳統的C風格的debug()函數:

>>> 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 

在這裏你能看到的debugLOAD_GLOBAL和消滅一切到相應CALL_FUNCTION

當然,除了最簡單化的使用模式外,對於你所做的事情的描述都比你真正需要的要簡單得多,但我認爲這是可行的。如果沒有人已經完成了它,會做出一個可愛的項目。

0

定義一個函數,什麼也不做,即

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

然後,只需重載所有你想要得到你的功能去掉的功能,ALA

logging.debug = nuzzing 
+0

我的原始問題包括此解決方案的一個變體 – 2010-01-06 07:56:37

+0

是的,但是通過讓您的佔位符函數以遞歸方式調用自身,您將立即開啓門(即超出最大通話深度棧)。 最近的cpython編譯器足夠聰明,可以減少任何只傳遞給noop的函數,所以任何對它的調用都會被有效地註釋掉。 我不認爲你會找到更好的解決方案。 – richo 2010-01-06 08:52:14

0

我喜歡「如果__debug_」解決方案除了把它放在每個電話前面都有點讓人分心和難看。我遇到了同樣的問題,並通過編寫一個腳本來克服它,該腳本會自動分析源文件,並用pass語句替換日誌語句(並註釋掉日誌語句的副本)。它也可以撤消此轉換。

我在生產環境中部署新代碼時使用它,因爲當生產環境中存在大量日誌報表時,我不需要這些報表並且它們影響性能。

您可以在這裏找到腳本:http://dound.com/2010/02/python-logging-performance/