python inspect.getsource 获取错误的源代码

3 投票
2 回答
1576 浏览
提问于 2025-04-18 04:19

运行这个文件 foo.py

import inspect

class Parent(object):
    def source1(self):
        class A(object):
            def foo(self):
                pass
        print inspect.getsource(A)
    def source2(self):
        class A(object):
            def bar(self, a, b, c):
                pass
        print inspect.getsource(A)


parent = Parent()
parent.source1()
parent.source2()

会产生这样的输出:

    class A(object):
        def foo(self):
            pass

    class A(object):
        def foo(self):
            pass

而我原本期待它能产生这样的输出:

    class A(object):
        def foo(self):
            pass

    class A(object):
        def bar(self, a, b, c):
            pass

我在测试中遇到了这个问题。我在测试方法里重复使用了 AB 作为类名。我正在测试一个依赖于 inspect.getsource 的函数,但结果并不如我所愿,因为后面的测试得到了之前命名的 AB 类的源代码。

2 个回答

2

这是因为 inspect.getsource 会在源文件中找到第一个匹配的 class A。根据 Python 2.7.6 中 inspect.py 的第 545 行,这个模式是用来查找源代码的。

pat = re.compile(r'^(\s*)class\s*' + name + r'\b')

所以如果你一开始在代码前面定义了 class A,就像这样:

import inspect

class A(object):
    pass

class Parent(object):
    def source1(self):
        class A(object):
            def foo(self):
                pass
        print 'id of A in source1 :', id(A)
        print inspect.getsource(A)
    def source2(self):
        class A(object):
            def bar(self, a, b, c):
                pass
        print 'id of A in source2 :', id(A)
        print inspect.getsource(A)

print 'id of A in global :', id(A)
parent = Parent()
parent.source1()
parent.source2()

那么结果将会是:

id of A in global : 12512848
id of A in source1 : 12415088
class A(object):
    pass

id of A in source2 : 12356672
class A(object):
    pass

即使它们的对象 id 是不同的。

2

inspect.getsource这个调用其实是去解析源文件,寻找类的定义,并返回它找到的第一个符合要求的类,这个类的缩进层级是最低的。换句话说,它不会按照你想要的那样去做。

这里有一个稍微不同的方法,可以得到你想要的输出,也许能满足你的需求:

import inspect

class Parent(object):
    def source1(self):
        class A1(object):
            def foo(self):
                pass
        A = A1
        print inspect.getsource(A)
    def source2(self):
        class A2(object):
            def bar(self, a, b, c):
                pass
        A = A2
        print inspect.getsource(A)


parent = Parent()
parent.source1()
parent.source2()

类的定义仍然可以用你想要的名字(比如“A”)来引用,但在源代码中的实际定义使用了不同的名字,这样getsource就能找到它了。

撰写回答