Установка переменных/атрибутов подкласса из базового класса

Можно ли динамически создавать/устанавливать переменные в подклассе внутри базового класса для нескольких подклассов, не затрагивая другие подклассы?

Например, возьмите этот код здесь:

class Base:
    @classmethod
    def __init__(cls,v):
        cls.v=v

class sub1(Base):
    Base.__init__(1)

class sub2(Base):
    Base.__init__(5)

В этом коде при создании sub1 его атрибут v равен 1. Но когда создается sub2, атрибут v как sub1, так и sub2 становится 5. Думаю, я знаю, почему это так. Я предполагаю, что @classmethod базового класса на самом деле устанавливает не атрибут подкласса, а его собственный атрибут. Затем этот атрибут наследуется в подклассах. Мой вопрос: как я могу использовать этот тип наследования для установки атрибутов подклассов, а не атрибутов базового класса, которые наследуются.

Другими словами, я хотел бы иметь возможность использовать аналогичную структуру, если это возможно (или, по крайней мере, простую), чтобы выполнить настройку атрибутов в подклассах, которые являются специфическими для подклассов, а не просто глобально унаследованы от базового класса. .

Это вообще возможно?


person Clayton Geist    schedule 13.04.2017    source источник
comment
Есть ли какая-то причина, по которой вы не можете просто сделать: v = 1 в sub1 и v = 5 в sub2? Присваивания в теле оператора класса вне любого метода уже определяют атрибуты класса; вам не нужен специальный метод, чтобы сделать это. Преобразование __init__ в classmethod звучит так, как будто вам действительно нужен метакласс, но это огромное излишество, если вы просто хотите установить атрибут класса...   -  person ShadowRanger    schedule 24.01.2018


Ответы (1)


Я не знаю, можно ли это легко сделать каким-либо другим способом, но я сам придумал решение, используя метакласс.

Решение:

class MetaBase(type):
    def __init__(cls, name, bases, namespace):
        super(MetaBase, cls).__init__(name, bases, namespace)
        if '_superclass' in namespace:  # A special attribute to distinguish between superclasses and subclasses
            if '_attrnames' not in namespace:
                raise AttributeError('"_attrnames" needs to be defined as a class attribute in a superclass.')
        else:
            if 'classvars' in namespace:  # Allow for define all required class attributes in one iterable attribute
                for attrname, attr in zip(getattr(cls.__mro__[1], '_attrnames'), getattr(cls, 'classvars')):  # Get all the varnames in the superclass's "varnames", as well as the values
                    setattr(cls, attrname, attr)
                    namespace[attrname] = attr
                delattr(cls, 'classvars')
            else:
                for attrname in getattr(cls.mro()[1], '_attrnames'):
                    if attrname not in namespace:
                        raise AttributeError('"%s" not defined, but is required.' % attrname)

class Base(metaclass=MetaBase):
    _superclass = True  # The value of this attribute doesn't matter
    _attrnames = ('a','b','c')

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

class Sub2(Base):
    classvars = (1, 2, 3)
person Clayton Geist    schedule 24.01.2018