Как я могу узнать, какой базовый класс добавляет определенный атрибут к объекту дочернего класса
Я работаю над проектом с длинной иерархией классов из нескольких модулей в разных файлах.
Я хочу знать, когда в цепочке наследования класс C получил атрибут A (тогда я могу получить модуль M, в котором определен C, и проверить код)
рассмотрим следующий пример кода, представьте, что все классы, кроме GrandChildOf1ChildOf2
находятся в других модулях, есть ли команда, что-то вроде: attribute_base(o4,'a')
с выходом: Base1
?
class SweetDir:
def __sweet_dir__(self):
"""
Same as dir, but will omit special attributes
:return: string
"""
full_dir = self.__dir__()
sweet_dir = []
for attribute_name in full_dir:
if not ( attribute_name.startswith('__')
and attribute_name.endswith('__')):
#not a special attribute
sweet_dir.append(attribute_name)
return sweet_dir
class Base1(SweetDir):
def __init__(self):
super(Base1,self).__init__()
self.a = 'a'
class Base2(SweetDir):
def __init__(self):
super(Base2,self).__init__()
self.b = 'b'
class ChildOf1 (Base1):
def __init__(self):
super(ChildOf1,self).__init__()
self.c = 'c'
class GrandChildOf1ChildOf2 (Base2,ChildOf1):
def __init__(self):
super(GrandChildOf1ChildOf2,self).__init__()
self.d = 'd'
o1 = Base1()
o2 = Base2()
o3 = ChildOf1()
o4 = GrandChildOf1ChildOf2()
print(o1.__sweet_dir__())
print(o2.__sweet_dir__())
print(o3.__sweet_dir__())
print(o4.__sweet_dir__())
выход:
['a']
['b']
['a', 'c']
['a', 'b', 'c', 'd']
1 ответ
Я не думаю, что есть встроенные функции для этого, но что-то подобное будет работать (это нуждается в улучшении):
def attribute_base(your_class, your_attr):
for base_class in your_class.__mro__:
if base_class != your_class:
tmp_inst = base_class()
if hasattr(tmp_inst, your_attr):
return base_class
Это вернет первый базовый класс вашего класса, который получил атрибут, который вы ищете. Это явно не идеально. Если два или более ваших базовых классов имеют одинаковый атрибут (с одинаковым именем), он может не возвращать фактический класс, в котором вы получили атрибут, но в вашем примере он будет работать.
[Обновление с комментарием AKX: использование __mro__
должен на самом деле решить эту проблему]
[Обновление: есть способ сделать это без экземпляра, следуя этому сильно документированному ответу: список-атрибутов-класса-без-экземпляра-объекта ]
from inspect import getmembers
def attribute_base(your_class, your_attr):
for base_class in your_class.__mro__:
if base_class != your_class:
members = [member[1].__code__.co_names for member in getmembers(base_class) if '__init__' in member and hasattr(member[1], "__code__")]
for member in members:
if your_attr in members:
return base_class
getmembers
дает вам каждый член класса, включая методы init, что мы и хотим. Нам нужно проверить, действительно ли это функция (hasattr(member[1], "__code__"))
потому что если нет __init__
Функция определена для класса (как, например, SweetDir в вашем примере), она будет возвращать wrapper_descriptor. Мы зациклились на членах в редком (возможном?) Случае есть несколько __init__
метод.