如何在Python中找到调用函数的类?

5 投票
4 回答
4661 浏览
提问于 2025-04-16 02:09

我想知道在Python中,调用某个函数的类是什么。比如,我有两个类,ClassA和ClassB。我想知道当classb_instance.call_class_a_method()调用class_a.class_a_method()时,调用者是谁。

class ClassA(object):
    def class_a_method(self):
        # Some unknown process would occur here to
        # define caller. 
        if caller.__class__ == ClassB:
            print 'ClassB is calling.'
        else:
            print 'ClassB is not calling.'

class ClassB(object):
    def __init__(self):
        self.class_a_instance = ClassA()
    def call_class_a_method(self):
        self.class_a_instance.class_a_method()

classa_instance = ClassA()
classa_instance.class_a_method()
classb_instance = ClassB()
classb_instance.call_class_a_method()

我希望输出的结果是:

'ClassB is not calling.'
'ClassB is calling.'

看起来inspect这个模块应该可以做到这一点,但我还没弄明白怎么用。

4 个回答

0

通常,调用者应该知道并使用正确的参数,而不是在被调用的类里做一些神秘的事情。

你是否需要一个工厂类或函数来记录这个类,或者写一个装饰器来处理这个问题呢?

7

好吧,如果你想查看调用栈,找出是哪个类(如果有的话)调用了它,这其实很简单:

def class_a_method(self):
    stack = inspect.stack()
    frame = stack[1][0]
    caller = frame.f_locals.get('self', None)

如果你想检查调用者是否是 ClassB 类型,应该使用 isinstance(caller, ClassB),而不是直接比较 __class__。不过,这种做法在 Python 中通常不太好,因为 Python 是一种鸭子类型(也就是说,关注的是对象的行为而不是对象的类型)。

正如其他人所说的,你的应用程序很可能需要重新设计,以避免使用 inspect.stack。这是 CPython 实现中的一个特性,并不能保证在其他 Python 实现中也存在。此外,这样做其实是错误的做法。

4

一个函数并不“属于”某个类——一个或多个类可能会(但不一定)引用某个特定的函数对象,比如:

def f(self): return g()

class One(object): bah = f

class Two(object): blup = f

def g(): ...

One.bahTwo.blup 都是指向同一个函数对象 f 的引用(当在类或它们的实例上访问时,会变成未绑定或绑定的方法对象,但在调用栈中不会留下痕迹)。所以,当你查看

One().f()

Two().f()

在这两种情况下调用的 g() 时,确实很难区分——就像下面的例子一样:

f('blah bloh blup')

完全没有涉及“类”的情况(尽管用作 fself 参数的字符串类型是会有的;-)。

我建议不要依赖反射——尤其是那种复杂的、需要很多经验法则的反射,因为要从中获取任何有用的信息对于生产代码来说是很麻烦的:最好重新设计代码以避免这种需求。

出于纯粹的调试目的,在这种情况下,你可以遍历调用栈,直到找到一个第一个参数名为 self 的调用函数,并检查绑定到这个参数名的值的类型——但正如我提到的,这完全是经验性的,因为没有什么强制要求你的调用者在它们的函数是某个类的方法时,必须把第一个参数命名为 self

这种经验性的反射会在我刚才给出的三个代码示例中产生类型对象 OneTwostr——如你所见,这种方法除了调试之外,确实没什么其他用处;-)。

如果你能更清楚地说明你通过这种反射尝试实现的具体目标,我们当然可以更好地帮助你。

撰写回答