Переменная класса V3 зависит от V1 и V2. Как определить V3 в детских классах

Parent Класс наследуется несколькими другими классами.

class Parent(object):

    V_1 = set()
    V_2 = set()

    ALL_V_ELEMENTS = V_1 | V_2

class Child1(Parent):
    V_1 = {1, }
    V_2 = {4, 7, 10}

class Child2(Parent):
    V_1 = {'a', 'b'}
    V_2 = {'a', 'c'}

V_1 а также V_2 различны у каждого ребенка (также они не меняются после создания класса).

Используя код ниже, я получаю то же значение для ALL_V_ELEMENTS:

print(Parent.ALL_V_ELEMENTS)  # prints: set()
print(Child1.ALL_V_ELEMENTS)  # prints: set()
print(Child2.ALL_V_ELEMENTS)  # prints: set()

Что-то, чего я не хочу. Что мне нужно, это:

print(Parent.ALL_V_ELEMENTS)  # prints: set()
print(Child1.ALL_V_ELEMENTS)  # prints: {1, 10, 4, 7}
print(Child2.ALL_V_ELEMENTS)  # prints: {'a', 'c', 'b'}

Для достижения моей цели я могу определить классы следующим образом:

class Child1(Parent):
    V_1 = {1, }
    V_2 = {4, 7, 10}
    ALL_V_ELEMENTS = V_1 | V_2

class Child2(Parent):
    V_1 = {'a', 'b'}
    V_2 = {'a', 'c'}
    ALL_V_ELEMENTS = V_1 | V_2

Однако копирование-вставка ALL_V_ELEMENTS = V_1 | V_2 на каждого ребенка Parent не кажется хорошей идеей

Другой альтернативой будет определение Parent по-другому:

class Parent(object):

    V_1 = set()
    V_2 = set()

    def __init__(self):
        self.ALL_V_ELEMENTS = self.V_1 | self.V_2

Это сделало бы | операция на каждом экземпляре, который является избыточным.


Есть ли лучший способ достичь моей цели?

1 ответ

Решение

Вы можете определить это как свойство:

class Parent(object):

    V_1 = set()
    V_2 = set()

    @property
    def ALL_V_ELEMENTS(self):
       return V_1 | V_2

Это, однако, будет пересчитывать набор каждый раз. Имея __init__ создать набор означает, что он будет создан для каждого экземпляра.

Вы можете вычислить набор в метаклассе, поэтому он создается только тогда, когда создается объект класса:

class AllVMeta(type):
    def __new__(typ, name, bases, attrs):
        cls = super(AllVMeta, typ).__new__(typ, name, bases, attrs)
        cls.ALL_V_ELEMENTS = cls.V_1 | cls.V_2
        return cls

Этот метакласс добавляет ALL_V_ELEMENTS объединение в любой подкласс; используйте это так:

class Parent(object, metaclass=AllVMeta):
    V_1 = set()
    V_2 = set()

class Child1(Parent):
    V_1 = {1, }
    V_2 = {4, 7, 10}

class Child2(Parent):
    V_1 = {'a', 'b'}
    V_2 = {'a', 'c'}

Демо-версия:

>>> class AllVMeta(type):
...     def __new__(typ, name, bases, attrs):
...         cls = super(AllVMeta, typ).__new__(typ, name, bases, attrs)
...         cls.ALL_V_ELEMENTS = cls.V_1 | cls.V_2
...         return cls
...
>>> class Parent(object, metaclass=AllVMeta):
...     V_1 = set()
...     V_2 = set()
...
>>> class Child1(Parent):
...     V_1 = {1, }
...     V_2 = {4, 7, 10}
...
>>> class Child2(Parent):
...     V_1 = {'a', 'b'}
...     V_2 = {'a', 'c'}
...
>>> Child1.ALL_V_ELEMENTS
{1, 10, 4, 7}
>>> Child2.ALL_V_ELEMENTS
{'a', 'c', 'b'}
Другие вопросы по тегам