Python 如何检查函数以检查双下划线的使用情况?

Python 如何检查函数以检查双下划线的使用情况?,python,python-3.x,Python,Python 3.x,我想检查一个函数对象,以了解该函数是否正在访问任何双下划线属性(例如,“\uuu name\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 对于这样的简单函数: In [11]: def foo(): import math; print(math.__doc__) In [12]: foo() This module is always available. It provides acces

我想检查一个函数对象,以了解该函数是否正在访问任何双下划线属性(例如,“\uuu name\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

对于这样的简单函数:

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\u 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__')
但是,如果函数连接“u”,或者更糟糕的是,使用字符或Unicode,那么在
dis
co_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', '_')

那么,是否有某种方法可以确保捕获函数中的所有双下划线访问?

您可以始终在代码上运行
pylint

正如所指出的,有一些方法可以绕过它的检查,但另一种选择,或者可能结合使用,将是在代码开始附近添加一些内容:

oldgetattr = getattr
def getattr(x,y):
    """ No __ access__ """
    assert not y.startswith('__')
    return oldgetattr(x,y)
然后运行代码-您可能还需要以类似的方式运行monkey patch
对象

以上内容在Python2.7.5+上进行了测试,似乎完成了这项工作

>def foo():导入数学;打印(getattr(数学,chr(95)*2+'doc'+'.'+chr(95)))
... 
>>>foo()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第1行,在foo中
文件“”,第3行,在getattr中
断言错误
>>> 

Python3提供了
\uuu getattribute\uuuu
方法来完全控制对类实例的访问。这不会解决内置类型问题,但可以让您检测对自己(传入)对象的访问

此外,还可以使用CodeObject将全局变量字典替换为自己的。因此,您可以使用getattribute方法扩展字典,并将其作为全局命名空间插入。这将使您能够看到全局访问。然后替换全局getattr


看看这需要多长时间。

我们是否假设您正在检查的函数不接受任何参数?否则,您可以使用例如
def foo(s):print(getattr(foo,s))
并调用
foo(“文档”)
,我认为您没有任何方法可以检测到这一点。我无法对函数进行任何假设,因此这是一个公平的观点。我很好奇,看看其他人对这种情况是否有建议。我怀疑你是否能找到一种不太麻烦的方法。遗憾的是,我认为你可能是对的。除了编写自定义getattr(或将其从名称空间中完全删除)之外,我不能确定是否知道对“dunder”属性的访问?它只捕捉数学,而不捕捉getattr(数学,'doc')。我会仔细观察,但我想知道它是否在走“标准”Python AST,在这种情况下,它不会捕获getattr类型的访问。谢谢,但不幸的是,范围不限于我自己的类。
>>> 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
>>>