问题 包含方法代码的类的名称


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

在我使用的下面的例子中 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()

8443
2018-05-06 14:12


起源

你在编码时知道这个,不是吗?它是: def test(self): print "A"。 - John Kugelman
我想避免复制类名。如果我更改了类名 A 至 AA 我将是第一个忘记擅长的人 print "A" 声明。 - user266940


答案:


在Python 3.x中,您可以简单地使用 __class__.__name__。该 __class__ 名字有点神奇,与此不一样 __class__ 的属性 self

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

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

7
2018-05-06 14:38





inspect.getmro 按顺序为您提供方法可能来自的类的元组。一旦你发现其中一个在其中有方法名称 dict, 你完成了:

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

4
2018-05-06 14:33



不幸的是,它为您提供了MRO中具有该名称属性的第一个类的名称,该名称不一定与您正在执行此操作的方法相同(如果您的方法距离MRO更远)。 - Thomas Wouters
很好的解决方案,但正如托马斯提到的那样,它不起作用 class B 覆盖了 test() 方法:A类:def init __(self):传递def测试(self):在inspect.getmro中导入inspect for c(self .__ class):如果'变量'在变量中(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. - user266940
对于之前评论中的混乱感到抱歉。这是我的第一个stackoverflow注释,我不知道无法在注释中添加代码。 - user266940
@Thomas,确切地说。我没有得到的是为什么这种行为应该是错误的:它找到完全相同的类(定义 test)如Python属性访问那样。 - Alex Martelli
确实如此,但这并不意味着它是OP想要的答案:) OP非常清楚地要求“上课” 这种方法 在“,not”中定义了定义具有此名称的方法的派生类最多的类。 - Thomas Wouters


使用 __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


2
2018-05-06 14:40



这与Alex的MRO解决方案有同样的问题。 - Thomas Wouters
同意。但如果事先知道只有单类继承,那么解决方案就可以了。谢谢你的说明。 - nkrkv


你可以使用(滥用?) 私人名称 实现这个效果。如果你查找属性 self 以...开头 __ 从方法内部,python更改名称 __attribute 至 _classThisMethodWasDefinedIn__attribute

只是以某种方式将你想要的类名存储在mangled-form中,方法可以看到它。作为一个例子,我们可以定义一个 __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

2
2018-05-06 15:33





你可以做

>>> 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。这不是最漂亮的,但它确实有效。


0
2018-05-06 16:33