Jeremy Hylton | a003316 | 2002-07-11 21:08:06 +0000 | [diff] [blame] | 1 | """Enumeration metaclass.""" |
| 2 | |
| 3 | class EnumMetaclass(type): |
| 4 | """Metaclass for enumeration. |
| 5 | |
| 6 | To define your own enumeration, do something like |
| 7 | |
| 8 | class Color(Enum): |
| 9 | red = 1 |
| 10 | green = 2 |
| 11 | blue = 3 |
| 12 | |
| 13 | Now, Color.red, Color.green and Color.blue behave totally |
| 14 | different: they are enumerated values, not integers. |
| 15 | |
| 16 | Enumerations cannot be instantiated; however they can be |
| 17 | subclassed. |
| 18 | """ |
| 19 | |
| 20 | def __init__(cls, name, bases, dict): |
| 21 | super(EnumMetaclass, cls).__init__(name, bases, dict) |
| 22 | cls._members = [] |
| 23 | for attr in dict.keys(): |
| 24 | if not (attr.startswith('__') and attr.endswith('__')): |
| 25 | enumval = EnumInstance(name, attr, dict[attr]) |
| 26 | setattr(cls, attr, enumval) |
| 27 | cls._members.append(attr) |
| 28 | |
| 29 | def __getattr__(cls, name): |
| 30 | if name == "__members__": |
| 31 | return cls._members |
| 32 | raise AttributeError, name |
| 33 | |
| 34 | def __repr__(cls): |
| 35 | s1 = s2 = "" |
| 36 | enumbases = [base.__name__ for base in cls.__bases__ |
| 37 | if isinstance(base, EnumMetaclass) and not base is Enum] |
| 38 | if enumbases: |
| 39 | s1 = "(%s)" % ", ".join(enumbases) |
| 40 | enumvalues = ["%s: %d" % (val, getattr(cls, val)) |
| 41 | for val in cls._members] |
| 42 | if enumvalues: |
| 43 | s2 = ": {%s}" % ", ".join(enumvalues) |
| 44 | return "%s%s%s" % (cls.__name__, s1, s2) |
| 45 | |
| 46 | class FullEnumMetaclass(EnumMetaclass): |
| 47 | """Metaclass for full enumerations. |
| 48 | |
| 49 | A full enumeration displays all the values defined in base classes. |
| 50 | """ |
| 51 | |
| 52 | def __init__(cls, name, bases, dict): |
| 53 | super(FullEnumMetaclass, cls).__init__(name, bases, dict) |
| 54 | for obj in cls.__mro__: |
| 55 | if isinstance(obj, EnumMetaclass): |
| 56 | for attr in obj._members: |
| 57 | # XXX inefficient |
| 58 | if not attr in cls._members: |
| 59 | cls._members.append(attr) |
| 60 | |
| 61 | class EnumInstance(int): |
| 62 | """Class to represent an enumeration value. |
| 63 | |
| 64 | EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves |
| 65 | like the integer 12 when compared, but doesn't support arithmetic. |
| 66 | |
| 67 | XXX Should it record the actual enumeration rather than just its |
| 68 | name? |
| 69 | """ |
| 70 | |
| 71 | def __new__(cls, classname, enumname, value): |
| 72 | return int.__new__(cls, value) |
| 73 | |
| 74 | def __init__(self, classname, enumname, value): |
| 75 | self.__classname = classname |
| 76 | self.__enumname = enumname |
| 77 | |
| 78 | def __repr__(self): |
| 79 | return "EnumInstance(%s, %s, %d)" % (self.__classname, self.__enumname, |
| 80 | self) |
| 81 | |
| 82 | def __str__(self): |
| 83 | return "%s.%s" % (self.__classname, self.__enumname) |
| 84 | |
| 85 | class Enum: |
| 86 | __metaclass__ = EnumMetaclass |
| 87 | |
| 88 | class FullEnum: |
| 89 | __metaclass__ = FullEnumMetaclass |
| 90 | |
| 91 | def _test(): |
| 92 | |
| 93 | class Color(Enum): |
| 94 | red = 1 |
| 95 | green = 2 |
| 96 | blue = 3 |
| 97 | |
| 98 | print Color.red |
| 99 | |
Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 100 | print repr(Color.red) |
Jeremy Hylton | a003316 | 2002-07-11 21:08:06 +0000 | [diff] [blame] | 101 | print Color.red == Color.red |
| 102 | print Color.red == Color.blue |
| 103 | print Color.red == 1 |
| 104 | print Color.red == 2 |
| 105 | |
| 106 | class ExtendedColor(Color): |
| 107 | white = 0 |
| 108 | orange = 4 |
| 109 | yellow = 5 |
| 110 | purple = 6 |
| 111 | black = 7 |
| 112 | |
| 113 | print ExtendedColor.orange |
| 114 | print ExtendedColor.red |
| 115 | |
| 116 | print Color.red == ExtendedColor.red |
| 117 | |
| 118 | class OtherColor(Enum): |
| 119 | white = 4 |
| 120 | blue = 5 |
| 121 | |
| 122 | class MergedColor(Color, OtherColor): |
| 123 | pass |
| 124 | |
| 125 | print MergedColor.red |
| 126 | print MergedColor.white |
| 127 | |
| 128 | print Color |
| 129 | print ExtendedColor |
| 130 | print OtherColor |
| 131 | print MergedColor |
| 132 | |
| 133 | def _test2(): |
| 134 | |
| 135 | class Color(FullEnum): |
| 136 | red = 1 |
| 137 | green = 2 |
| 138 | blue = 3 |
| 139 | |
| 140 | print Color.red |
| 141 | |
Walter Dörwald | 70a6b49 | 2004-02-12 17:35:32 +0000 | [diff] [blame] | 142 | print repr(Color.red) |
Jeremy Hylton | a003316 | 2002-07-11 21:08:06 +0000 | [diff] [blame] | 143 | print Color.red == Color.red |
| 144 | print Color.red == Color.blue |
| 145 | print Color.red == 1 |
| 146 | print Color.red == 2 |
| 147 | |
| 148 | class ExtendedColor(Color): |
| 149 | white = 0 |
| 150 | orange = 4 |
| 151 | yellow = 5 |
| 152 | purple = 6 |
| 153 | black = 7 |
| 154 | |
| 155 | print ExtendedColor.orange |
| 156 | print ExtendedColor.red |
| 157 | |
| 158 | print Color.red == ExtendedColor.red |
| 159 | |
| 160 | class OtherColor(FullEnum): |
| 161 | white = 4 |
| 162 | blue = 5 |
| 163 | |
| 164 | class MergedColor(Color, OtherColor): |
| 165 | pass |
| 166 | |
| 167 | print MergedColor.red |
| 168 | print MergedColor.white |
| 169 | |
| 170 | print Color |
| 171 | print ExtendedColor |
| 172 | print OtherColor |
| 173 | print MergedColor |
| 174 | |
| 175 | if __name__ == '__main__': |
| 176 | _test() |
| 177 | _test2() |