Python自省:如何检测方法访问了哪些字段

Posted

技术标签:

【中文标题】Python自省:如何检测方法访问了哪些字段【英文标题】:Python introspection: how to detect what fields are accessed from a method 【发布时间】:2012-01-11 10:38:09 【问题描述】:

我需要用 Python 执行一些黑魔法。

按照我之前的问题Lazy data-flow (spreadsheet like) properties with dependencies in Python,现在我想通过检查计算函数自动填充派生属性所依赖的字段列表。

我想我可以使用func_code.co_names 属性,但我不太确定那里是否有一些警告,documentation 并没有多大帮助。

有什么想法吗?

【问题讨论】:

【参考方案1】:

不幸的是,func_code.co_names 不太可能有太大帮助。这包含代码段内访问的所有名称,包括全局变量,按出现顺序排列。

class Test(object):
    def calc_a(self):
        return self.b + self.c

    def calc_x(self):
        return self.y.a + self.y.b

>>> Test.calc_a.func_code.co_names
('b', 'c')
>>> Test.calc_x.func_code.co_names
('y', 'a', 'b')

无法从这个数组中判断“a”和“b”是从“self”还是从“self.y”加载的。一般来说,不执行一段代码就知道其访问模式的唯一方法是反汇编它。

>>> import dis
>>> dis.dis(Test.calc_x)
 23           0 LOAD_FAST                0 (self)
              3 LOAD_ATTR                0 (y)
              6 LOAD_ATTR                1 (a)
              9 LOAD_FAST                0 (self)
             12 LOAD_ATTR                0 (y)
             15 LOAD_ATTR                2 (b)
             18 BINARY_ADD          
             19 RETURN_VALUE        

我们看到该函数加载“self”变量(对于绑定函数,它始终为co_varnames[0]),然后从该对象加载属性“y”(co_names[0]),然后从该对象加载属性“a”(co_names[1])。从 self.y.b 中推送第二个堆栈对象,然后将两者相加。

查看标准库中dis.py的源码,看看C Python二进制代码是如何反汇编的。第 0 个变量的负载对于绑定函数很重要。另一个方便的点是函数的参数是 co_varnames[:co_argcount](其余或 varnames 是本地变量),co_freevars 是来自封闭非全局范围的变量。

【讨论】:

以上是关于Python自省:如何检测方法访问了哪些字段的主要内容,如果未能解决你的问题,请参考以下文章

第三十四篇 Python面向对象之 反射(自省)

如何自省 Django 中的属性和模型字段?

自省/反射

Python_反射

如何在 Python 2.x 中对对象执行自省?

如何在运行时检测 Python 版本? [复制]