【问题标题】:How to get the caller class name inside a function of another class in python?如何在python中另一个类的函数中获取调用者类名?
【发布时间】:2013-06-08 12:59:06
【问题描述】:

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

#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()

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

【问题讨论】:

  • “调用者类”是什么意思。你是说aAb还是B
  • @mgilson 我的意思是当代码在 B 类(被调用者)下的方法“def Bad”中运行时,我必须能够打印检索调用者类的名称,即在这种情况下为“A”。
  • @mgilson 我可以打印“inspect.stack()[1][3]”语句,这只会让我得到调用者函数。
  • 类似的,相关的,有趣的:get a class name of calling method

标签: python stack runtime sequence-diagram


【解决方案1】:

好吧,在对提示进行了一些挖掘之后,这就是我得到的:

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()

不确定当从对象以外的东西调用时它会如何表现。

【讨论】:

  • 做得很好。在某些时候,我想更加熟悉代码和框架对象......
  • 请注意,这不适用于静态或类方法,因为 self 然后不会被定义,或者它不会是类的实例(很可能)
  • 该方法的这项工作是通过子类中的super 调用的。或者至少它不会是你所期望的,因为type(self) 将是子类的类型,而不是实际拥有该方法的类的类型。
  • 获取调用者类的更准确方法是stack[1][0].f_locals["__class__"]。返回的是调用类,而不是第一个参数的类。
  • 这对我来说非常有用。我只想表示诚挚的感谢。
【解决方案2】:

使用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”。这正是我所需要的。
【解决方案3】:

也许这破坏了某些 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 概念。

【讨论】:

    【解决方案4】:

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

    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
    

    【讨论】:

      【解决方案5】:

      可以使用方法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
      

      【讨论】:

      • 这将打印出类似I was called by &lt;class '__main__.YourClass'&gt;.your_method 的内容。要从接受的答案中获得相同的结果,请将变量 the_class 更改为 prev_frame.f_locals['self'].__class__.__name__
      【解决方案6】:

      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')
      

      【讨论】:

      • 从未想过调用方法或从调用者的类访问属性是可能的。这提供了有趣的灵活性。
      • @jlwise24 你说得对,这很有趣,但有时速度很慢。你必须小心,尤其是在 Python 中。阅读编程中的“内省和反思”。
      猜你喜欢
      • 2010-10-28
      • 1970-01-01
      • 2016-12-28
      • 1970-01-01
      • 2012-03-18
      • 1970-01-01
      • 2015-08-06
      • 1970-01-01
      • 2015-11-13
      相关资源
      最近更新 更多