2016-09-12 2 views
6

У меня есть большое количество автоматически сгенерированные классов в Python, которые по существу представляют собой перечисления для части коммуникационного протокола, они выглядят такМожете ли вы динамически добавлять переменные класса в подкласс python?

# definitions.py 

class StatusCodes(ParamEnum): 
    Success = 1 
    Error = 2 
    UnexpectedAlpaca = 3 

class AlpacaType(ParamEnum): 
    Fuzzy = 0 
    ReallyMean = 1 

# etc etc etc 

Определения вещи таким образом, делает его легким для автогенератора, а также для людей, чтобы изменить. Класс ParamEnum предоставляет все функциональные возможности, получение/настройку, сравнение, преобразование и создание из входящих сетевых данных и т. Д.

Однако эти классы требуют дополнительных метаданных. Я не хочу добавить это в определение источника каждого класса, хотя, как это делает его менее читаемым и сломаю автогенератор

На данный момент я делаю это, как этот

# param_enum.py 

class ParamEnum(object): 
    def __init__(self): 
     self.__class__._metadata = get_class_metadata(self.__class__) 

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

I попытался добавить это в нижнюю часть файла определения, но столкнулся с проблемами там тоже.

class StatusCodes(ParamEnum): 
    Success = 1 
    Error = 2 
    UnexpectedAlpaca = 3 

for var in locals(): # or globals? 
    add_metadata(var) 
    #doesn't work because it is in the same file, modifying dict while iteratng 

Есть ли способ в Python переопределения/добавления функциональности до момента, когда определен класс, который может быть унаследован подклассы? В идеале хотелось бы что-то вроде

class ParamEnum(object): 
    def __when_class_defined__(cls): 
     add_metadata(cls) 
+1

В Python классы являются экземплярами их метакласса. То есть вы можете добиться этого, предоставив свой ParamEnum Metaclass, переопределяющий '__new__'. – Phillip

ответ

5

Используйте класс декоратора

def add_metadata(cls): 
    cls._metadata = get_class_metadata(cls) 
    return cls 

@add_metadata 
class StatusCodes(ParamEnum): 
    Success = 1 
    Error = 2 
    UnexpectedAlpaca = 3 

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

Если вы не знакомы с декораторами, пожалуйста, прочитайте this (хотя и немного длинный).

Вы также можете использовать метаклассы, но они привносят больше сложностей, чем декоратор. Если вы хотите избежать дополнительной отделочной линии @add_metadata и считаете ее избыточной по отношению к указанному подклассу ParamEnum, вы также можете сделать _metadataa descriptor с ленивой оценкой в ​​базовом классе ParamEnum.

class MetadataDescriptor(object): 
    def __get__(self, obj, cls): 
     if cls._metadata_value is None: 
      cls._metadata_value = get_class_metadata(cls) 
     return cls._metadata_value 

class ParamEnum(object): 
    _metadata_value = None 
    _metadata = MetadataDescriptor() 
+0

Я считаю, что дескриптор лучше декоратора. –

+0

@AlbertLee Я тоже думаю, что дескриптор более изящный. Тем не менее, это довольно продвинутая «магия», которая может быть препятствием в зависимости от того, кто поддерживает код. –

+0

Пошел с дескриптором, работает очень красиво и сохраняет мой автогенерированный файл в чистоте. Благодаря! –

Смежные вопросы