2014-01-20 3 views
4

Есть ли способ в Python 3.2 или более поздней, чтобы определить класс, подклассы которого должны быть созданы с использованием определенного метакласса без создания класса с использованием этого метакласса?Использовать метаклассы только для подклассов

Пример, чтобы продемонстрировать, что я имею в виду: допустим, я хочу создать класс Enum, подклассы которого могут использоваться для определения типов перечислений. Тип перечисления будет иметь фиксированное количество экземпляров, каждый из которых имеет отличное значение int (или другое). Тип перечисления будет объявлен путем создания подкласса Enum и назначения значений атрибутам этого класса. Через метакласс Enum значения этих атрибутов будут заменены экземплярами нового класса.

Класс Enum может также определять некоторые методы или элементы класса или экземпляра, которые могут использоваться в его подклассах или их экземплярах.

class EnumMeta(type): 
    def __new__(mcs, what, bases, dict): 
     cls = super().__new__(mcs, what, bases, { }) 

     cls._instances_by_value = { } 

     for k, v in dict.items(): 
      if k.startswith('__') or k == 'get_by_value': 
       setattr(cls, k, v) 
      else: 
       instance = cls(k, v) 

       setattr(cls, k, instance) 
       cls._instances_by_value[v] = instance 

     return cls 


class Enum(metaclass = EnumMeta): 
    def __init__(self, name, value): 
     self.name = name 
     self.value = value 

    def __repr__(self): 
     return '{}.{}'.format(type(self).__name__, self.name) 

    @classmethod 
    def get_by_value(cls, value): 
     return cls._instances_by_value[value] 

Пример создания такого типа перечисления:

class Boolean(Enum): 
    true = 0 
    false = 1 
    file_not_found = 2 

print(Boolean.true) # Prints Boolean.true 
print(Boolean.get_by_value(1)) # Prints Boolean.false 

В определении EnumMeta.__new__, вы можете увидеть, что я должен был исключить get_by_value из обработки метакласса. Я часто сталкиваюсь с этой проблемой, когда я хочу каким-то образом обработать элементы определения класса с помощью метакласса.

Каков предпочтительный подход для исключения базового класса (Enum в примере) от обработки метаклассом (EnumMeta в примере) при использовании метакласса для всех подклассов базового класса?

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

ответ

5

Ваш «родитель» Enum не будет иметь никаких оснований, только классы, производные от Enum, будут иметь непустые кортежи bases. Используйте это легко выручать рано, чтобы создать базовый класс с регулярным __new__ вызова:

class EnumMeta(type): 
    def __new__(mcs, what, bases, dict): 
     if not bases: 
      # Enum itself 
      return super().__new__(mcs, what, bases, dict) 

     # Process subclass creation 
+1

Это действительно очень чистое решение. Как бы вы справились с ситуацией, когда «Enum' _does_ имеют базовые классы? Проверить точное соответствие «основания»? – Feuermurmel

+0

Да, проверка на точный матч тоже сработает. В конце концов, это то, что мы делаем здесь. –

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