| """Enumeration metaclass. |
| |
| XXX This is very much a work in progress. |
| |
| """ |
| |
| import string |
| |
| class EnumMetaClass: |
| """Metaclass for enumeration. |
| |
| To define your own enumeration, do something like |
| |
| class Color(Enum): |
| red = 1 |
| green = 2 |
| blue = 3 |
| |
| Now, Color.red, Color.green and Color.blue behave totally |
| different: they are enumerated values, not integers. |
| |
| Enumerations cannot be instantiated; however they can be |
| subclassed. |
| |
| """ |
| |
| def __init__(self, name, bases, dict): |
| """Constructor -- create an enumeration. |
| |
| Called at the end of the class statement. The arguments are |
| the name of the new class, a tuple containing the base |
| classes, and a dictionary containing everything that was |
| entered in the class' namespace during execution of the class |
| statement. In the above example, it would be {'red': 1, |
| 'green': 2, 'blue': 3}. |
| |
| """ |
| for base in bases: |
| if base.__class__ is not EnumMetaClass: |
| raise TypeError, "Enumeration base class must be enumeration" |
| bases = filter(lambda x: x is not Enum, bases) |
| self.__name__ = name |
| self.__bases__ = bases |
| self.__dict = {} |
| for key, value in dict.items(): |
| self.__dict[key] = EnumInstance(name, key, value) |
| |
| def __getattr__(self, name): |
| """Return an enumeration value. |
| |
| For example, Color.red returns the value corresponding to red. |
| |
| XXX Perhaps the values should be created in the constructor? |
| |
| This looks in the class dictionary and if it is not found |
| there asks the base classes. |
| |
| The special attribute __members__ returns the list of names |
| defined in this class (it does not merge in the names defined |
| in base classes). |
| |
| """ |
| if name == '__members__': |
| return self.__dict.keys() |
| |
| try: |
| return self.__dict[name] |
| except KeyError: |
| for base in self.__bases__: |
| try: |
| return getattr(base, name) |
| except AttributeError: |
| continue |
| |
| raise AttributeError, name |
| |
| def __repr__(self): |
| s = self.__name__ |
| if self.__bases__: |
| s = s + '(' + string.join(map(lambda x: x.__name__, |
| self.__bases__), ", ") + ')' |
| if self.__dict: |
| list = [] |
| for key, value in self.__dict.items(): |
| list.append("%s: %s" % (key, int(value))) |
| s = "%s: {%s}" % (s, string.join(list, ", ")) |
| return s |
| |
| |
| class EnumInstance: |
| """Class to represent an enumeration value. |
| |
| EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves |
| like the integer 12 when compared, but doesn't support arithmetic. |
| |
| XXX Should it record the actual enumeration rather than just its |
| name? |
| |
| """ |
| |
| def __init__(self, classname, enumname, value): |
| self.__classname = classname |
| self.__enumname = enumname |
| self.__value = value |
| |
| def __int__(self): |
| return self.__value |
| |
| def __repr__(self): |
| return "EnumInstance(%s, %s, %s)" % (`self.__classname`, |
| `self.__enumname`, |
| `self.__value`) |
| |
| def __str__(self): |
| return "%s.%s" % (self.__classname, self.__enumname) |
| |
| def __cmp__(self, other): |
| return cmp(self.__value, int(other)) |
| |
| |
| # Create the base class for enumerations. |
| # It is an empty enumeration. |
| Enum = EnumMetaClass("Enum", (), {}) |
| |
| |
| def _test(): |
| |
| class Color(Enum): |
| red = 1 |
| green = 2 |
| blue = 3 |
| |
| print Color.red |
| print dir(Color) |
| |
| print Color.red == Color.red |
| print Color.red == Color.blue |
| print Color.red == 1 |
| print Color.red == 2 |
| |
| class ExtendedColor(Color): |
| white = 0 |
| orange = 4 |
| yellow = 5 |
| purple = 6 |
| black = 7 |
| |
| print ExtendedColor.orange |
| print ExtendedColor.red |
| |
| print Color.red == ExtendedColor.red |
| |
| class OtherColor(Enum): |
| white = 4 |
| blue = 5 |
| |
| class MergedColor(Color, OtherColor): |
| pass |
| |
| print MergedColor.red |
| print MergedColor.white |
| |
| print Color |
| print ExtendedColor |
| print OtherColor |
| print MergedColor |
| |
| if __name__ == '__main__': |
| _test() |