如何在Python另一个类的函数中获取调用者类名?

49

我的目标是刺激一个应用程序的序列图,为此我需要在运行时获取调用者和被调用类的名称信息。我可以成功地检索到调用函数,但无法获取调用类名称?

#Scenario caller.py:

import inspect

class A:

    def Apple(self):
        print "Hello"
        b=B()
        b.Bad()



class B:

    def Bad(self):
        print"dude"
        print inspect.stack()


a=A()
a.Apple()

当我打印堆栈时,没有关于调用类的信息。那么在运行时是否可能检索调用类?


“Caller class”是什么意思?你是指aAb还是B - mgilson
@mgilson 我的意思是,当代码在类B(被调用者)下的方法“def Bad”中运行时,我必须能够打印并检索调用者类的名称,在这种情况下是“A”。 - Kaushik
@mgilson 我可以打印“inspect.stack()[1] [3]”语句,这将使我仅获取调用函数。 - Kaushik
类似、相关、有趣的内容可参见:获取调用方法的类名 - artu-hnrq
6个回答

66

好的,在查找了一些提示后,这是我得到的结果:

stack = inspect.stack()
the_class = stack[1][0].f_locals["self"].__class__.__name__
the_method = stack[1][0].f_code.co_name

print("I was called by {}.{}()".format(the_class, the_method))
# => I was called by A.a()

调用时:

➤ python test.py
A.a()
B.b()
  I was called by A.a()

假设有文件test.py:

import inspect

class A:
  def a(self):
    print("A.a()")
    B().b()

class B:
  def b(self):
    print("B.b()")
    stack = inspect.stack()
    the_class = stack[1][0].f_locals["self"].__class__.__name__
    the_method = stack[1][0].f_code.co_name
    print("  I was called by {}.{}()".format(the_class, the_method))

A().a()

不确定在从非对象调用时它会如何行事。


1
干得好。在某个时候,我想更加熟悉代码和框架对象... - mgilson
5
请注意,这也不适用于静态方法或类方法,因为此时 self 没有被定义,或者它不是该类的实例(很可能)。 - mgilson
2
这个方法的工作是通过子类中的 super 调用的。或者至少不会是你期望的,因为 type(self) 将是子类的类型,而不是实际拥有该方法的类的类型。 - rmorshea
2
获取调用者类的更准确方法是stack[1][0].f_locals["__class__"]。返回的是调用类,而不是第一个参数的类。 - Elijah
2
这对我来说非常有用。我只想表示真挚的感谢。 - duma
显示剩余2条评论

8
使用来自Python: How to retrieve class information from a 'frame' object?的答案,我得到了类似于这样的东西...
import inspect

def get_class_from_frame(fr):
  args, _, _, value_dict = inspect.getargvalues(fr)
  # we check the first parameter for the frame function is
  # named 'self'
  if len(args) and args[0] == 'self':
    # in that case, 'self' will be referenced in value_dict
    instance = value_dict.get('self', None)
    if instance:
      # return its class
      return getattr(instance, '__class__', None)
  # return None otherwise
  return None


class A(object):

    def Apple(self):
        print "Hello"
        b=B()
        b.Bad()

class B(object):

    def Bad(self):
        print"dude"
        frame = inspect.stack()[1][0]
        print get_class_from_frame(frame)


a=A()
a.Apple()

这给我输出以下结果:

Hello
dude
<class '__main__.A'>

显然,这返回一个对类本身的引用。如果你想要类的名字,可以从__name__属性中获取。

不幸的是,这对于类方法或静态方法无效...


这种方法其实更完美,因为它检查了 "self",如果没有则显示“None”。这正是我所需要的。 - Kaushik

6

可以使用inspect.currentframe()方法而不是索引inspect.stack()的返回值,这样可以避免索引。

prev_frame = inspect.currentframe().f_back
the_class = prev_frame.f_locals["self"].__class__
the_method = prev_frame.f_code.co_name

1
这将打印类似于 I was called by <class'__main__.YourClass'>.your_method 的内容。要从接受的答案中获得相同的结果,请将变量 the_class 更改为 prev_frame.f_locals['self'].__class__.__name__ - igorkf

5

也许这会违反一些Python编程规范,但如果 Bad 总是要检查调用者的类,为什么不在调用时将调用者的 __class__ 作为参数传递给它呢?

class A:

    def Apple(self):
        print "Hello"
        b=B()
        b.Bad(self.__class__)



class B:

    def Bad(self, cls):
        print "dude"
        print "Calling class:", cls


a=A()
a.Apple()

结果:

Hello
dude
Calling class: __main__.A

如果这种方式不好,并且使用inspect确实是获取调用者类的首选方法,请解释为什么。我还在学习更深入的Python概念。

1
Python 3.8
import inspect


class B:
    def __init__(self):
        if (parent := inspect.stack()[1][0].f_locals.get('self', None)) and isinstance(parent, A):
            parent.print_coin()


class A:
    def __init__(self, coin):
        self.coin: str = coin
        B()

    def print_coin(self):
        print(f'Coin name: {self.coin}')


A('Bitcoin')

1
从来没想过可以从调用者的类中调用方法或访问属性。这提供了有趣的灵活性。 - JWCompDev
@jlwise24 你说得对,这很有趣,但有时候会很慢。你必须小心,特别是在Python中。阅读关于编程中的“内省和反射”的内容。 - Джон Смит

0

将堆栈中的类实例名称存储到类变量中:

import inspect

class myClass():

    caller = ""

    def __init__(self):
        s = str(inspect.stack()[1][4]).split()[0][2:]
        self.caller = s

    def getInstanceName(self):
        return self.caller

这个

myClassInstance1 = myClass()
print(myClassInstance1.getInstanceName())

将会打印:

myClassInstance1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接