【问题标题】:get a class name of calling method获取调用方法的类名
【发布时间】:2019-04-08 17:38:10
【问题描述】:

我知道如何获取调用者方法名称(来自这里:How to get the caller's method name in the called method?

import sys
print sys._getframe().f_back.f_code.co_name

我想得到的是这个方法所属的类名(假设它在类中)。 所以如果:

def get_some_info():
    print('Class name of caller:', XXX)

class Base:
     def my_method(self):
         get_some_info()

class A(Base):
     pass

class B(Base):
     pass

a = A()
b = B()
a.my_method()
b.my_method()

应该返回:

 ... A
 ... B

xxx我该怎么办?

我尝试(使用_getframe 上的信息)执行以下操作:

 sys._getframe().f_back.f_code.__self__

但它不起作用

更新:

我不能将类名传递给被调用的函数(否则这很容易,但感谢所有提出此解决方案的人!)

【问题讨论】:

  • 为什么不将get_some_info 设为类方法并打印出self.__name__ 变量?它应该给你类名....
  • @toti08 或者如果需要实例方法,他可以调用self.__class__.__name__
  • 哇,您的问题具有误导性。您不是在寻找定义调用方法的类。您正在寻找 self 变量的类...
  • @Aran-Fey: 是的,这就是为什么在我想得到的输出中,我提到它应该是AB,而不是Base

标签: python python-3.x


【解决方案1】:

可以通过inspect.currentframe()获取调用frame对象,通过f_locals属性获取self绑定的对象:

import inspect

def get_some_info():
    # get the call frame of the calling method
    frame = inspect.currentframe().f_back
    try:
        # try to access the caller's "self"
        try:
            self_obj = frame.f_locals['self']
        except KeyError:
            return None

        # get the class of the "self" and return its name
        return type(self_obj).__name__
    finally:
        # make sure to clean up the frame at the end to avoid ref cycles
        del frame

这样做的缺点是它依赖于第一个参数被命名为“self”。在某些情况下,我们会使用不同的名称,例如在编写元类时:

class MyMeta(type):
    def __call__(cls, *args, **kwargs):
        get_some_info()  # won't work!

如果你有一个带有self 变量的函数,它可能会产生意想不到的结果:

def not_a_method():
    self = 3
    print(get_some_info())  # output: int

我们可以解决这两个问题,但需要做很多工作。我们可以通过调用代码对象的co_varnames 属性来检查“self”参数的名称。而为了检查调用函数是否真的是类中定义的方法,我们可以遍历self的MRO,尝试找到调用我们的方法。最终的结果就是这个怪物:

def get_some_info():
    # get the call frame of the calling method
    frame = inspect.currentframe().f_back
    try:
        # find the name of the first variable in the calling
        # function - which is hopefully the "self"
        codeobj = frame.f_code
        try:
            self_name = codeobj.co_varnames[0]
        except IndexError:
            return None

        # try to access the caller's "self"
        try:
            self_obj = frame.f_locals[self_name]
        except KeyError:
            return None

        # check if the calling function is really a method
        self_type = type(self_obj)
        func_name = codeobj.co_name

        # iterate through all classes in the MRO
        for cls in self_type.__mro__:
            # see if this class has a method with the name
            # we're looking for
            try:
                method = vars(cls)[func_name]
            except KeyError:
                continue

            # unwrap the method just in case there are any decorators
            try:
                method = inspect.unwrap(method)
            except ValueError:
                pass

            # see if this is the method that called us
            if getattr(method, '__code__', None) is codeobj:
                return self_type.__name__

        # if we didn't find a matching method, return None
        return None
    finally:
        # make sure to clean up the frame at the end to avoid ref cycles
        del frame

这应该可以正确处理几乎所有你扔给它的东西:

class Base:
    def my_method(whatever):
        print(get_some_info())

    @functools.lru_cache()  # could be any properly implemented decorator
    def my_decorated_method(foo):
        print(get_some_info())

    @classmethod
    def my_class_method(cls):
        print(get_some_info())

class A(Base):
    pass

def not_a_method(self=3):
    print(get_some_info())

A().my_method()            # prints "A"
A().my_decorated_method()  # prints "A"
A.my_class_method()        # prints "None"
not_a_method()             # prints "None"
print(get_some_info())     # prints "None"

【讨论】:

    【解决方案2】:

    你可以使用inspect.stack():

    def get_some_info():
        _stack = inspect.stack()[1]
        print ('cls:', _stack[0].f_locals['self'].__class__.__name__, 'func:', _stack[3])
    
    ....
    
    a = A()
    b = B()
    a.my_method()
    b.my_method()
    

    打印:

    ('cls:', 'A', 'func:', 'my_method')
    ('cls:', 'B', 'func:', 'my_method')
    

    【讨论】:

    • 请注意,这不适用于类方法和静态方法(显然也不适用于普通函数)
    • @brunodesthuilliers 好吧,OP 正在寻找self的类型,所以这是给定的......
    • @Aran-Fey 确实如此,但我认为仍然值得一提。
    • self 实际上不需要被称为self,但我从未见过这样的案例,除了旨在展示这种可能性的练习。好吧,那和元数据。
    猜你喜欢
    • 2015-12-19
    • 2012-07-31
    • 1970-01-01
    • 2018-07-12
    • 1970-01-01
    • 2013-07-07
    • 2019-01-07
    • 2011-01-17
    • 2019-10-15
    相关资源
    最近更新 更多