Как я могу узнать, какой базовый класс добавляет определенный атрибут к объекту дочернего класса - PullRequest
0 голосов
/ 22 ноября 2018

Я работаю над проектом с длинной иерархией классов из нескольких модулей в разных файлах.

Я хочу знать, когда в цепочке наследования класс 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 Ответ

0 голосов
/ 22 ноября 2018

Я не думаю, что для этого есть встроенные функции, но что-то подобное сработало бы (это требует улучшений):

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__ действительно должно решить эту проблему]

[Обновление: есть способ сделать это без экземпляра, следуя этому сильно документированному ответу: list-the-attribute-of-a-class-без объекта-экземпляра-объекта ]

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__ методов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...