У меня есть несколько классов, которые сохраняются в ZODB. Все классы должны иметь общие функции индексации, но также все они определяют множество свойств. Свойства определяются на уровне класса с дескрипторами, чтобы иметь возможность перехватывать запись в них для проверки и проверки дубликатов в БД. Для этого у каждого класса есть список автоматически поддерживаемых свойств, _properties. Они также содержат _indices и некоторые другие функции, которые я не хочу копировать отдельно для каждого класса Element (около 10).

Рассмотрим следующий код:

class BaseElement(object):
    _properties = None

class ElementFlavour1(BaseElement):
    _properties = 'a', 'b', 'c'

....

class ElementFlavourN(BaseElement):
    _properties = 'e', 'f', 'g'

for item in locals():
    if issubclass(item, BaseElement):
        item._v_properties_set = set(item._properties) if item._properties else set()

Итак, он в основном делает то, что должен: мне нужно линейно перебирать _properties, но я также хочу выполнять быстрый поиск, если в классе присутствует какое-либо свойство. Тем не менее, я не хочу повторяться и явно указывать этот набор для каждого отдельного варианта BaseElement.

Текущее решение работает, но я считаю его уродливым приемом и хотел бы от него избавиться. Например. подклассы BaseElement в других модулях не получат правильной настройки своих свойств. Игра с __new__ также кажется неправильным, поскольку он перехватывает не конструкцию класса, а создание экземпляра. Итак, как это сделать правильно?

P.S. Я знаю об OrderedDict, но это не то, что я ищу. Я хотел бы аккуратно подключиться к процессу создания класса и добавить туда произвольную функциональность.

1
Michael 29 Сен 2012 в 13:30
1
Это может быть полезно: stackoverflow.com/questions / 100003 /…
 – 
Blender
29 Сен 2012 в 13:31

1 ответ

Лучший ответ

Вы можете сделать это с метаклассом:

class BaseType(type):
    def __init__(cls, name, bases, clsdict):
        super(BaseType, cls).__init__(name, bases, clsdict)
        setattr(cls,'_v_properties_set',
                set(cls._properties) if cls._properties else set())

class BaseElement(object):
    __metaclass__ = BaseType    
    _properties = None

class ElementFlavour1(BaseElement):
    _properties = 'a', 'b', 'c'

class ElementFlavourN(BaseElement):
    _properties = 'e', 'f', 'g'

print(ElementFlavourN._v_properties_set)
# set(['e', 'g', 'f'])

Вы также можете сделать это с помощью декоратора класса, но тогда вам придется декорировать каждый подкласс BaseElement индивидуально. Метакласс наследуется, поэтому вам нужно определить его только один раз.

4
unutbu 29 Сен 2012 в 13:50
Именно то, что мне нужно, спасибо! И благодаря ссылке на Blenders я наконец понимаю магию, стоящую за этим.
 – 
Michael
29 Сен 2012 в 14:27