Функциональность метакласса на всех наследников, исключая базовый класс

У меня в приложении есть класс для наследования остальных подклассов. Одна из его задач в том, что он передаёт генерацию класса своему метаклассу, чтобы кроме наследования не нужно было дополнительно прописывать metaclass=SmthMeta.

class ClassesMeta(type):
    def __new__(mcs, clsname, bases, dct):
        dct['b'] = dct.get('a', 0) + 10
        return super(ClassesMeta, mcs).__new__(mcs, clsname, bases, dct)


class Base(metaclass=ClassesMeta):
    a = 5


class Child(Base):
    a = 10


Base.b  # 15
Child.b  # 20

Но проблема в том, что в логике моего приложения класс Base должен иметь атрибут a = None, но всегда имеет переопределённый атрибут у детей, а также класс Base никогда не используется кроме как для наследования.

Но код

class Base(metaclass=ClassesMeta):
    a = None

class Child(Base):
    a = 10

ожидаемо выдаст ошибку во время инициализации Base:

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    class Base(metaclass=ClassesMeta):
  File "<pyshell#1>", line 3, in __new__
    dct['b'] = dct.get('a', 0) + 10
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

Итого: мне нужно сделать применение метакласса не непосредственно к классу с metaclass=ClassesMeta, а ко всем, наследованным от него. Пока что моё решение в том, чтобы фильтровать имя класса:

class ClassesMeta(type):
    def __new__(mcs, clsname, bases, dct):
        if clsname == 'Base':
            return type.__new__(mcs, clsname, bases, dct)

        dct['b'] = dct.get('a', 0) + 10
        return super(ClassesMeta, mcs).__new__(mcs, clsname, bases, dct)

Но это выглядит как большой костыль. Есть ли способ убрать для Base генерацию атрибута b более правильным способом?


Ответы (1 шт):

Автор решения: JK_Action

Ошибка у Вас в том что метод get для словаря вернет значение по умолчанию если ключа не существует, а он есть просто равен None.

class ClassesMeta(type):
    def __new__(mcs, clsname, bases, dct):
        val = dct.get('a', 0)
        if val is None:
            val = 0
        dct['b'] = val + 10
        return super(ClassesMeta, mcs).__new__(mcs, clsname, bases, dct)
→ Ссылка