Получить поля, объявленные в subclass

Рейтинг: 1Ответов: 2Опубликовано: 16.07.2023

Допустим, есть класс:

class Base:
    a = 1

    def subclass_fields(self):
        return ... # здесь должно вернуться что-то типа {"b": 2}

и есть класс который отнаследовался от Base:

class MyClass(Base):
    b = 2

Я ожидаю, что метод subclass_fields вернёт те поля и методы, которых нет в классе Base, но которые были объявлены в каком-либо из сабклассов - например, при объявлении нового класса MyClass2:

class MyClass2(MyClass):
    c = 3

c = MyClass2()
print(c.subclass_fields) # {"b": 2, "c": 3}

Ответы

▲ 1Принят

После изучения исходников django orm нашёл способ получения полей наследников:

class Field:
    """ Класс поля которое мы будем искать в исходниках """
    my_field = True

Метакласс:

class Meta(type):
    def __new__(cls, name, bases, namespace, *kwargs):
        super_new = super().__new__
        fields = {}
        for b in bases:
            if hasattr(b, "_fields"):
                for k, v in b._fields.items():
                    fields[k] = v

        for k, v in namespace.items():
            if hasattr(v, "my_field"):
                fields[k] = v

        namespace["_fields"] = fields

        return super_new(cls, name, bases, namespace)

базовый класс от которого будут наследоваться все остальные классы:

class Base(metaclass=Meta):
    pass

Теперь создаём классы, наследуясь от Base:

class MyClass(Base):
    a = Field()

class MyClass2(MyClass):
    b = Field()

получаем список полей у наследников:

mc = MyClass2()
print(mc._fields)
▲ 2

Могу предложить такой способ:

class Base:
    a = 1
    
class MyClass(Base):
    b = 2
    
class MyClass2(MyClass):
    c = 3

def get_subclacc_fields(obj):
    k = v = None
    for k, v in globals().items():
        try:
            if isinstance(obj, v):
                my_dir = set(dir(obj.__class__)) - set(dir(v)) # тут получаем все поля, которых нет у базового parent-а
                return {name: mc.__getattribute__(name) for name in my_dir} # а тут бежим по ним и забираем значения
        except:
            pass
    return dict()

mc = MyClass2()
print(get_subclacc_fields(mc))

Вывод:

{'c': 3, 'b': 2}

UPD: Однако имя функции мне не очень нравится. Оно говорит, что мы получаем поля класса-наследника, хотя на самом деле она возвращает поля, которых нет в главном базовом классе.