EDITED QUESTION
I\'m trying to create a class factory that can generate enumeration-like classes with the following properties:
Maybe this enumeration function from the Verse Quiz program could be of some use to you: Verse Quiz
You can create new classes on-the-fly with the type builtin:
type(name, bases, dict)
Return a new type object. This is essentially a dynamic form of the class statement. The name string is the class name and becomes the
__name__
attribute; the bases tuple itemizes the base classes and becomes the__bases__
attribute; and the dict dictionary is the namespace containing definitions for class body and becomes the__dict__
attribute. For example, the following two statements create identical type objects:>>> class X(object): ... a = 1 ... >>> X = type('X', (object,), dict(a=1)) New in version 2.2.
In this case:
genre_mapping = { }
for genre in { 'scifi', 'romance', 'comic', 'science' }:
genre_mapping[ 'genre' ] = type( genre, ( Genre, ), { } )
or in Python 2.7+:
genre_mapping = { genre: type( genre, ( Genre, ), { } ) for genre in genres }
If you are doing this a lot, you can abstract away the pattern.
>>> def enum( cls, subs ):
... return { sub: type( sub, ( cls, ), { } ) for sub in subs }
...
>>> enum( Genre, [ 'scifi', 'romance', 'comic', 'science' ] )
{'romance': <class '__main__.romance'>, 'science': <class '__main__.science'>,
'comic': <class '__main__.comic'>, 'scifi': <class '__main__.scifi'>}
EDIT: Have I missed the point? (I've not used SQLAlchemy before.) Are you asking how to create new subclasses of Genre
, or how to create new instances? The former seems intuitively right, but the latter is what you've asked for. It's easy:
list( map( Genre, [ 'scifi', ... ] ) )
will make you a list of:
[ Genre( 'scifi' ), ... ]
I think that this satisfies all of your specified requirements. If not, we can probably add whatever you need.
def enum(classname, values):
class EnumMeta(type):
def __iter__(cls):
return cls._instances.itervalues()
class EnumType(object):
__metaclass__ = EnumMeta
_instances = {}
_next_id = 0
def __init__(self, value):
self.value = value
self.id = type(self)._next_id
type(self)._next_id += 1
def instance(self, value):
return type(self)._instances[value]
cls = type(classname, (EnumType, ), {})
instances = dict((value, cls(value)) for value in values)
cls._instances = instances
def __new__(cls, value):
raise TypeError('No more instances allowed')
cls.__new__ = staticmethod(__new__)
return cls
Genre = enum('Genre', ['scifi', 'comic', 'science'])
for item in Genre:
print item, item.value, item.id
assert(item is Genre(item.value))
assert(item is item.instance(item.value))
Genre('romance')
In response to your comment on Noctis Skytower's answer wherein you say that you want Genre.comic = Genre('comic')
(untested):
class Genre(GenreBase):
genres = ['comic', 'scifi', ... ]
def __getattr__(self, attr):
if attr in type(self).genres:
self.__dict__[attr] = type(self)(attr)
return self.__dict__[attr]
This creates an instance of genre in response to an attempt to access it and attaches it to the instance on which it is requested. If you want it attached to the entire class, replace the line
self.__dict__[attr] == type(self)(attr)
with
type(self).__dict__[attr] = type(self)(attr)
this has all subclasses create instances of the subclass in response to requests as well. If you want subclasses to create instances of Genre
, replace type(self)(attr)
with Genre(attr)