2014-01-14 57 views
1

我想檢查一個函數對象,以知道函數是否正在訪問任何雙下劃線屬性(例如'__name__','__doc__'等)。如何檢查函數以檢查雙下劃線用法?

對於一個簡單的功能是這樣的:

In [11]: def foo(): import math; print(math.__doc__) 

In [12]: foo() 
This module is always available. It provides access to the 
mathematical functions defined by the C standard. 

我可以看看LOAD_ATTR在反彙編輸出:

In [13]: dis.dis(foo) 
    1   0 LOAD_CONST    1 (0) 
       3 LOAD_CONST    0 (None) 
       6 IMPORT_NAME    0 (math) 
       9 STORE_FAST    0 (math) 
      12 LOAD_GLOBAL    1 (print) 
      15 LOAD_FAST    0 (math) 
      18 LOAD_ATTR    2 (__doc__) 
      21 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      24 POP_TOP    
      25 LOAD_CONST    0 (None) 
      28 RETURN_VALUE 

即使功能使用簡單getattr,我可以分析dis輸出或看看功能代碼的co_consts

In [19]: def foo(): import math; print(getattr(math, '__doc__')) 

In [20]: dis.dis(foo) 
    1   0 LOAD_CONST    1 (0) 
       3 LOAD_CONST    0 (None) 
       6 IMPORT_NAME    0 (math) 
       9 STORE_FAST    0 (math) 
      12 LOAD_GLOBAL    1 (print) 
      15 LOAD_GLOBAL    2 (getattr) 
      18 LOAD_FAST    0 (math) 
      21 LOAD_CONST    2 ('__doc__') 
      24 CALL_FUNCTION   2 (2 positional, 0 keyword pair) 
      27 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      30 POP_TOP    
      31 LOAD_CONST    0 (None) 
      34 RETURN_VALUE   

In [21]: foo.__code__.co_consts 
Out[21]: (None, 0, '__doc__') 

但是,如果函數連接'_',或者更糟糕的是,使用字符或unicode,那麼在disco_consts甚至ast中似乎沒有明顯的方法來捕獲這些。

In [22]: def foo(): import math; print(getattr(math, chr(95)*2 + 'doc' + '_' + chr(95))) 

In [23]: foo() 
This module is always available. It provides access to the 
mathematical functions defined by the C standard. 

In [24]: dis.dis(foo) 
    1   0 LOAD_CONST    1 (0) 
       3 LOAD_CONST    0 (None) 
       6 IMPORT_NAME    0 (math) 
       9 STORE_FAST    0 (math) 
      12 LOAD_GLOBAL    1 (print) 
      15 LOAD_GLOBAL    2 (getattr) 
      18 LOAD_FAST    0 (math) 
      21 LOAD_GLOBAL    3 (chr) 
      24 LOAD_CONST    2 (95) 
      27 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      30 LOAD_CONST    3 (2) 
      33 BINARY_MULTIPLY  
      34 LOAD_CONST    4 ('doc') 
      37 BINARY_ADD   
      38 LOAD_CONST    5 ('_') 
      41 BINARY_ADD   
      42 LOAD_GLOBAL    3 (chr) 
      45 LOAD_CONST    2 (95) 
      48 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      51 BINARY_ADD   
      52 CALL_FUNCTION   2 (2 positional, 0 keyword pair) 
      55 CALL_FUNCTION   1 (1 positional, 0 keyword pair) 
      58 POP_TOP    
      59 LOAD_CONST    0 (None) 
      62 RETURN_VALUE   

In [25]: foo.__code__.co_consts 
Out[25]: (None, 0, 95, 2, 'doc', '_') 

那麼,有什麼辦法可以肯定地捕捉函數中的所有雙下劃線訪問嗎?

+0

我們是否假設您正在檢查的函數不接受任何參數?否則,你可以有例如'def foo(s):print(getattr(foo,s))'並調用'foo('__ doc __')',我不認爲你有任何方式來檢測它。 – senshin

+0

我不能對這個函數做任何假設,所以這是一個公平點。我很好奇,看看別人是否有這方面的建議。 – capitalistcuttle

+0

我懷疑你會找到任何方法來做到這一點,而不是比它的價值更麻煩。 – BrenBarn

回答

0

您可以隨時在您的代碼上運行pylint

正如指出的有圍繞其檢查方式,但另一種選擇,或者可能結合,將沿着增加,代碼附近開始的路線的東西:

oldgetattr = getattr 
def getattr(x,y): 
    """ No __ access__ """ 
    assert not y.startswith('__') 
    return oldgetattr(x,y) 

然後運行代碼 - 您可能還需要以類似的方式修補object.__getattribute__

上面是有關python 2.7.5+測試並似乎做工作

>>> def foo(): import math; print(getattr(math, chr(95)*2 + 'doc' + '_' + chr(95))) 
... 
>>> foo() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 1, in foo 
    File "<stdin>", line 3, in getattr 
AssertionError 
>>> 
+0

爲了檢查受保護的訪問權限(W0212),我認爲?它僅捕獲數學.__ doc__而不是getattr(數學,'__doc__')。我會仔細觀察,但是我不知道它是否正在走「標準」Python AST,在這種情況下,它不會捕獲getattr類型的訪問。 – capitalistcuttle

0

Python 3中提供用於在訪問類實例總控制的__getattribute__方法。這不會解決內建類型,但它可以讓你檢測自己(傳入)對象的訪問。

此外,你可以用你自己的CodeObject代替全局變量字典。因此,您可以使用getattribute方法擴展字典並將其作爲全局名稱空間插入。這應該讓你看到全球訪問。然後替換全局getattr?

看看你有多遠。

+0

謝謝,但不幸的是,範圍不限於我自己的班級。 – capitalistcuttle