【问题标题】:name of the class that contains the method code包含方法代码的类的名称
【发布时间】:2010-05-06 14:12:07
【问题描述】:

我正在尝试查找包含方法代码的类的名称。

在下面的示例中,我使用self.__class__.__name__,但当然这会返回self 是实例的类的名称,而不是包含test() 方法代码的类。 b.test() 将打印 'B' 而我想得到 'A'

我查看了inspect 模块文档,但没有发现任何直接有用的东西。

class A:
  def __init__(self):
    pass
  def test(self):
    print self.__class__.__name__

class B(A):
  def __init__(self):
    A.__init__(self)



a = A()
b = B()

a.test()
b.test()

【问题讨论】:

  • 你在编码的时候就知道了,不是吗?这是一个:def test(self): print "A"
  • 我想避免复制类名。如果我将类名从A 更改为AA,我将是第一个忘记熟练使用print "A" 语句的人。

标签: python introspection


【解决方案1】:

在 Python 3.x 中,您可以简单地使用 __class__.__name____class__ 名称有点神奇,与 self__class__ 属性不同。

在 Python 2.x 中,没有获得该信息的好方法。您可以使用堆栈检查来获取代码对象,然后遍历类层次结构寻找正确的方法,但是它缓慢而乏味,并且可能会在您不希望它时中断。您还可以使用元类或类装饰器以某种方式对类进行后处理,但这两种方法都是相当侵入性的。你可以做一些非常丑陋的事情,比如访问self.__nonexistant_attribute,捕获 AttributeError 并从损坏的名称中提取类名。如果您只是想避免输入两次名称,那么这些方法都不值得。至少忘记更新名称可以通过执行以下操作变得更加明显:

class C:
    ...
    def report_name(self):
        print C.__name__

【讨论】:

    【解决方案2】:

    inspect.getmro 按顺序为您提供方法可能来自的类的元组。只要您找到其中一个在其dict 中包含方法名称的方法,您就完成了:

    for c in inspect.getmro(self.__class__):
        if 'test' in vars(c): break
    return c.__name__
    

    【讨论】:

    • 不幸的是,它为您提供了 MRO 中第一个具有该名称属性的类的名称,这不一定与您正在执行此操作的方法相同(如果您的方法更远向下 MRO。)
    • 不错的解决方案,但正如 Thomas 所提到的,当 class B 覆盖 test() 方法时它不起作用:A 类:def init__(self): pass def test(self): import在 inspect.getmro(self.__class) 中检查 c: if 'test' in vars(c): break print c.__name__ class B(A): def __init__(self): A.__init__(self) def test(self): A.test(self) a = A() b = B() a.test() # 打印 A b.test() # 打印 B
    • 抱歉之前的评论一团糟。这是我的第一个 stackoverflow 评论,我不知道无法在 cmets 中添加代码。
    • @Thomas,没错。我不明白为什么这种行为应该是错误的:它找到与 Python 属性访问完全相同的类(定义 test)。
    • 确实如此,但这并不意味着它是 OP 想要的答案 :) OP 很清楚地要求“类 this method 是在其中定义的”,而不是“定义具有此名称的方法的最派生类”。
    【解决方案3】:

    使用类对象本身的__dict__

    class A(object):
        def foo(self):
            pass
    
    class B(A):
        pass
    
    def find_decl_class(cls, method):
        if method in cls.__dict__:
            return cls
        for b in cls.__bases__:
            decl = find_decl_class(b, method)
            if decl:
                return decl
    
    print 'foo' in A.__dict__
    print 'foo' in B.__dict__
    print find_decl_class(B, 'foo').__name__
    

    将打印 True、False、A

    【讨论】:

    • 这和 Alex 的 MRO 解决方案有同样的问题。
    • 同意。但是如果事先知道只有单类继承,解决方案就OK,也很简单。感谢您的来信。
    【解决方案4】:

    您可以使用(滥用?)private name mangling 来实现此效果。如果您从方法内部查找self 上以__ 开头的属性,python 会将名称从__attribute 更改为_classThisMethodWasDefinedIn__attribute

    只是以某种方式将您想要的类名存储在方法可以看到的地方。例如,我们可以在执行此操作的基类上定义 __new__ 方法:

    def mangle(cls, attrname):
        if not attrname.startswith('__'):
            raise ValueError('attrname must start with __')
        return '_%s%s' % (cls.__name__, attrname)
    
    class A(object):
    
        def __new__(cls, *args, **kwargs):
            obj = object.__new__(cls)
            for c in cls.mro():
                setattr(obj, mangle(c, '__defn_classname'), c.__name__)
            return obj
    
        def __init__(self):
            pass
    
        def test(self):
            print self.__defn_classname
    
    class B(A):
    
        def __init__(self):
            A.__init__(self)
    
    
    
    a = A()
    b = B()
    
    a.test()
    b.test()
    

    哪个打印:

    A
    A
    

    【讨论】:

      【解决方案5】:

      你可以的

      >>> class A(object):
      ...   def __init__(self):
      ...     pass
      ...   def test(self):
      ...     for b in self.__class__.__bases__:
      ...       if hasattr(b, 'test'):
      ...         return b.__name__
      ...     return self.__class__.__name__
      ... 
      >>> class B(A):
      ...   def __init__(self):
      ...     A.__init__(self)
      ...
      >>> B().test()
      'A'
      >>> A().test()
      'A'
      >>> 
      

      请记住,您可以使用__class__.__base__ 来简化它,但如果您使用多重继承,这个版本会更好。

      它只是首先检查其基类中的test。这不是最漂亮的,但它确实有效。

      【讨论】:

        猜你喜欢
        • 2011-11-25
        • 2019-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-22
        • 1970-01-01
        • 2017-03-15
        相关资源
        最近更新 更多