РЕДАКТИРОВАНИЕ ВОПРОСА
Я пытаюсь создать фабрику классов, которая может генерировать перечислимые классы со следующими свойствами:
- Класс инициализированиз списка разрешенных значений (т. е. он автоматически генерируется!).
- Класс создает один экземпляр для каждого из разрешенных значений.
- Класс не позволяет создавать дополнительные экземпляры.после завершения вышеуказанного шага (любая попытка сделать это приводит к исключению).
- Экземпляры класса предоставляют метод, который при заданном значении возвращает ссылку на соответствующий экземпляр.
- Классэкземпляры имеют только два атрибута: идентификатор и значение.Атрибут id автоматически увеличивается для каждого нового экземпляра;значение атрибута - это значение, которое представляет экземпляр.
- Класс является итеративным.Я предпочел бы реализовать это, используя принятый ответ на другой вопрос SO (в частности, используя реестр классов и определяя метод iter в метаклассе, из которого создаются мои классы перечисления).
Это все, что я ищу.Пожалуйста, рассмотрите исходный текст (ниже) просто как фон для вопроса.Извините за непонятность с самого начала.
ОБНОВЛЕННЫЙ ОТВЕТ
Я внес небольшие изменения в очень полезный ответ от aaronasterling.Я думал, что покажу это здесь, чтобы другие могли извлечь выгоду, и чтобы я получил больше комментариев, если я сделал что-то не так:)
Изменения, которые я сделал:
(0) Портированыв p3k (iteritems -> items, metaclass -> 'metaclass =', нет необходимости указывать объект в качестве базового класса)
(1) Изменен метод экземпляра на @classmethod (теперьМне не нужен объект для его вызова, просто класс)
(2) Вместо заполнения _registry одним махом, я обновляю его каждый раз, когда создается новый элемент.Это означает, что я могу использовать его длину для установки идентификатора, и поэтому я избавился от атрибута _next_id.Это также будет работать лучше для расширения, которое я планирую (см. Ниже).
(3) Удален параметр имени класса из enum ().В конце концов, это имя класса будет локальным именем;в любом случае глобальное имя должно быть установлено отдельно.Поэтому я использовал фиктивный «XXX» в качестве локального имени класса.Я немного беспокоюсь о том, что происходит, когда я вызываю функцию во второй раз, но, похоже, она работает.Если кто-нибудь знает почему, дайте мне знать.Если это плохая идея, я, конечно, могу автоматически генерировать новое локальное имя класса при каждом вызове.
(4) Расширил этот класс, чтобы предоставить возможность, с помощью которой новые элементы enum могут добавляться пользователем.В частности, если instance () вызывается с несуществующим значением, соответствующий объект создается и затем возвращается методом.Это полезно, если я получаю большое количество значений перечисления при разборе файла.
def enum(values):
class EnumType(metaclass = IterRegistry):
_registry = {}
def __init__(self, value):
self.value = value
self.id = len(type(self)._registry)
type(self)._registry[value] = self
def __repr__(self):
return self.value
@classmethod
def instance(cls, value):
return cls._registry[value]
cls = type('XXX', (EnumType, ), {})
for value in values:
cls(value)
def __new__(cls, value):
if value in cls._registry:
return cls._registry[value]
else:
if cls.frozen:
raise TypeError('No more instances allowed')
else:
return object.__new__(cls)
cls.__new__ = staticmethod(__new__)
return cls
ORIGINAL TEXT
Я использую SQLAlchemy в качестве объектно-реляционного отображенияинструмент.Это позволяет мне отображать классы в таблицы в базе данных SQL.
У меня есть несколько классов.Один класс (Книга) - это ваш типичный класс с некоторыми данными экземпляра.Остальные (Жанр, Тип, Обложка и т. Д.) Являются по существу типом перечисления;например, жанр может быть только «фантастика», «романтика», «комикс», «наука»;Покрытие может быть только «твердым», «мягким»;и так далее.Между Книгой и каждым другим классом существует отношение многие-к-одному.
Я хотел бы полуавтоматически генерировать каждый из классов стиля перечисления.Обратите внимание, что SQLAlchemy требует, чтобы scifi был представлен как экземпляр класса Genre;Другими словами, было бы не просто определить Genre.scifi = 0, Genre.romance = 1 и т. д.
Я попытался написать перечисление метакласса, которое принимает в качестве аргументов имя класса исписок допустимых значений.Я надеялся, что
Genre = enum('Genre', ['scifi', 'romance', 'comic', 'science'])
создаст класс, который позволяет эти конкретные значения, а также обходит и создает каждый из объектов, которые мне нужны: Genre ('scifi'), Genre ('romance')и т. д.
Но я застрял.Одна конкретная проблема заключается в том, что я не могу создать Genre ('scifi'), пока ORM не узнает об этом классе;с другой стороны, к тому времени, когда ORM узнает о Genre, мы уже не в конструкторе классов.
Кроме того, я не уверен, что мой подход хорош для начала.
Любой совет будет оценен.