获取调用方法的类名

时间:2018-11-05 11:00:53

标签: python python-3.x

我知道如何获取调用者方法的名称(从这里: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__

但这不起作用

更新:

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

2 个答案:

答案 0 :(得分:5)

您可以使用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')

答案 1 :(得分:5)

您可以使用inspect.currentframe()获取调用框架对象,并通过其self属性获取f_locals所绑定的对象:

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"
相关问题