Имя класса, содержащего код метода
Я пытаюсь найти имя класса, содержащего код метода.
В приведенном ниже примере я использую 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()
Ответы
Ответ 1
В Python 3.x вы можете просто использовать __class__.__name__
. Имя __class__
мягко магия, а не то же, что и атрибут __class__
self
.
В 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__
Ответ 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
Ответ 4
Вы можете использовать (злоупотреблять?) личное имя 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
. Это не самый красивый, но он работает.