带有 stackframe/inspection 的嵌套函数的字符串表示

问题描述 投票:0回答:1

我正在努力为大型代码库生成调用图,并且我想为嵌套函数/闭包生成一个很好的字符串表示形式 - 例如对于此功能,类似于

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>
通过这种方法获得了,但我想知道是否有更优雅的方法来做到这一点?

python trace
1个回答
0
投票

您可以使用

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']
© www.soinside.com 2019 - 2024. All rights reserved.