Skip to content Skip to sidebar Skip to footer

Python Enumeration Class For ORM Purposes

EDITED QUESTION I'm trying to create a class factory that can generate enumeration-like classes with the following properties: Class is initialized from the list of allowed values

Solution 1:

new answer based on updates

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')

old answer

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)


Solution 2:

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' ), ... ]

Solution 3:

Maybe this enumeration function from the Verse Quiz program could be of some use to you: Verse Quiz


Post a Comment for "Python Enumeration Class For ORM Purposes"