反射调用对象

22 投票
4 回答
14808 浏览
提问于 2025-04-17 01:05

我想知道如何在 b.func() 这个函数里查看 A 的实例(也就是 A 的实例中的 self):

class A():
    def go(self):
        b=B()
        b.func()

class B():
    def func(self):
        # Introspect to find the calling A instance here

4 个回答

1

通过重构你的代码,使其像下面这样工作:

class A():
    def go(self):
        b = B(self)
        b.func()

class B():
    def __init__(self, a):
        self.a = a

    def func(self):
        # Use self.a

或者

class A():
    def go(self):
        b = B()
        b.func(self)

class B():
    def func(self, a):
        # a
5

你把它作为参数传给 b.func()

43

一般来说,我们不希望 func 能够访问调用它的 A 实例,因为这样会破坏封装性。也就是说,在 b.func 里面,你应该能够访问传入的参数和关键字参数,以及实例 b 的状态和属性(通过 self),还有任何全局变量。

如果你想知道哪个对象在调用,合理的方法有:

  1. 把调用的对象作为参数传给函数
  2. 在使用 func 之前,明确地把调用者的引用添加到 b 实例上,然后通过 self 来访问这个引用。

不过,在说完这些注意事项后,还是值得一提的是,Python 的反射能力很强,有时候可以访问调用模块。在 CPython 的实现中,你可以在不改变现有函数签名的情况下,访问调用的 A 实例:

class A:
    def go(self):
        b=B()
        b.func()

class B:
    def func(self):
        import inspect
        print inspect.currentframe().f_back.f_locals["self"]
        
if __name__ == "__main__":
    a = A()
    a.go()

输出:

<__main__.A instance at 0x15bd9e0>

这个技巧在调试时可能会很有用。类似的技术在标准库的 logging 模块中也有使用,可以在这里看到,这样日志记录器就能发现源代码、文件名、行号和函数名,而不需要明确传递这些上下文信息。然而,在正常情况下,如果 B.func 实际上需要使用 A,直接访问调用栈通常不是一个明智的设计选择,因为传递所需的信息会更清晰、更简单,而不是试图“回溯”到调用者。

撰写回答