In my previous post, I disussesed with the reader the concept of metaclass and its internals. and in following post one usage of the python metaclass is discussed. Today another use of the metaclass will be discussed.
As a note, this article is inspired from this original post from stackoverflow.
python does not have internal keyword for enumeration, however, with the help of metaclass and dictionary, you can create your own enuemration.
let's see two impl, one is with a enum method by which you can further create any enumeration class.
The impl
# define the __metaclass__ function def M_add_class_attr(attribs): def foo(name, bases, dict_): for v, k in attribs: dict_[k] = v return type(name, bases, dict_) return foo def enum(names): class Foo(object): __metaclass__ = M_add_class_attr(enumerate(names)) def __setattr__(self, name, value): # this make it readonly raise NotImplementedError return Foo()
the test code
import unittest from MetaClass.EnumImpl1 import M_add_class_attr, enum class Test(unittest.TestCase): def test_enumImpl1_usage_example(self): Animal = enum(['DOG', 'CAT']) self.assertEqual(0, Animal.DOG) self.assertEqual(1, Animal.CAT) if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.test_enumImpl1_usage_example'] unittest.main()
In this example, we are still going to create a animal enumeration class, and we want Animal to be a symbol rather than a variable where the client is able to reassign to.
the impl
class Animal(object): values = ["Horse", "Dog", "Cat"] # this is a very neat implementation # where you define a __metaclass__ method that # has the __getattr__ override to return the index of the constants # that you have defined in class instance 'values' class __metaclass__(type): def __getattr__(self, name): return self.values.index(name) # the above is equivalent as writting as follow. # class Animal_Metaclass(type): # def __getattr__(self, name): # return self.values.index(name) # __metaclass__ = Animal_Metaclass
the test code
import unittest from MetaClass.EnumImpl2 import Animal class Test(unittest.TestCase): # though the syntax is not well recognized by the Eclispe IDE, the function works # perfectly well. def test_enumImpl2_usage(self): # print Animal.Horse self.assertEqual(0, Animal.Horse) self.assertEqual(1, Animal.Dog) self.assertEqual(2, Animal.Cat) if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.test_enumImpl2_usage'] unittest.main()