имя класса, содержащего код метода - PullRequest
11 голосов
/ 06 мая 2010

Я пытаюсь найти имя класса, содержащего код метода.

В приведенном ниже примере я использую 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()

Ответы [ 5 ]

7 голосов
/ 06 мая 2010

В Python 3.x вы можете просто использовать __class__.__name__.Имя __class__ является слегка волшебным и не совпадает с атрибутом __class__ self.

. В Python 2.x нет хорошего способа получить эту информацию.Вы можете использовать проверку стека для получения объекта кода, а затем пройтись по иерархии классов в поисках правильного метода, но он медленный и утомительный и, вероятно, сломается, когда вы этого не захотите.Вы также можете использовать метакласс или декоратор класса для пост-обработки класса каким-либо образом, но оба они являются довольно навязчивыми подходами.И вы можете сделать что-то действительно уродливое, например, получить доступ к self.__nonexistant_attribute, перехватить AttributeError и извлечь имя класса из искаженного имени.Ни один из этих подходов не стоит того, если вы просто хотите не вводить имя дважды;хотя бы забыть обновить имя можно сделать немного более очевидным, сделав что-то вроде:

class C:
    ...
    def report_name(self):
        print C.__name__
4 голосов
/ 06 мая 2010

inspect.getmro дает вам по порядку кортеж классов, откуда может прийти метод Как только вы найдете один из них с именем метода в dict, все готово:

for c in inspect.getmro(self.__class__):
    if 'test' in vars(c): break
return c.__name__
2 голосов
/ 06 мая 2010

Вы можете использовать (оскорбления?) искажение личного имени для достижения этого эффекта.Если вы посмотрите на атрибут 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
2 голосов
/ 06 мая 2010

Используйте __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

0 голосов
/ 06 мая 2010

Вы можете сделать

>>> 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. Это не самая красивая, но работает.

...