我正在努力为大型代码库生成调用图,并且我想为嵌套函数/闭包生成一个很好的字符串表示形式 - 例如对于此功能,类似于
Test.a.b
:
class Test:
def a(self):
def b():
pass
目前我有以下内容:
import inspect
def trace(frame):
code = frame.f_code
module = inspect.getmodule(code)
module_name = module.__name__ if module else None
try:
class_name = frame.f_locals["self"].__class__.__name__
except (KeyError, AttributeError):
class_name = None
func_name = code.co_name
print(module_name, class_name, func_name)
如果我从我的测试类中调用它:
class Test:
def a(self):
trace(inspect.currentframe())
def b():
trace(inspect.currentframe())
b()
def main():
test = Test()
test.a()
然后我得到以下输出:
__main__ Test a
__main__ None b
有没有办法检测
b
是否在Test.a
内定义?
我知道可以通过函数本身的字符串表示来实现这一点,例如
<function Test.a.<locals>.b at 0x105976fc0>
通过这种方法获得了,但我想知道是否有更优雅的方法来做到这一点?
您可以使用
code
对象来实现这一点,无需任何额外的导入。
这是一个基本示例,请注意,对于更复杂的情况,在函数内部声明类的情况下,代码会失败,但可以相应地进行修改......通过修改递归。
def cls_struct(cls):
# get all code objects of the specified class
codes = []
for m_name in dir(cls):
# skeep dunnder methods
if not (m_name.startswith('__') and m_name.endswith('__')):
m = getattr(cls, m_name)
# method only, no attr
if callable(m):
codes.append(m.__code__)
res = []
def __cls_struct(codes):
nested_codes = []
for c in codes:
if c is None or isinstance(c, str):
# if a nested class is met (as string identifier)
pass
elif c.co_consts != (None,):
nested_codes.extend(
__cls_struct(c.co_consts[1:])
)
return res
return __cls_struct(codes)
测试用例
# nested function declaration
class Test:
def a(self):
def b():
def c():
pass
print(cls_struct(Test))
['Test.a', 'Test.a.<locals>.b', 'Test.a.<locals>.b.<locals>.c']
# inheritance with nested class declaration
class Test2(Test):
def b(self):
class __Test: # <- nested class,
pass
print(cls_struct(Test2))
['Test.a', 'Test.a.<locals>.b', 'Test2.b', 'Test2.b.<locals>.__Test']