diff options
Diffstat (limited to 'Demo/metaclasses/Enum.py')
-rw-r--r-- | Demo/metaclasses/Enum.py | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/Demo/metaclasses/Enum.py b/Demo/metaclasses/Enum.py new file mode 100644 index 0000000..71a8e52 --- /dev/null +++ b/Demo/metaclasses/Enum.py @@ -0,0 +1,165 @@ +"""Enumeration metaclass.""" + +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() |