|
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 |
|
100 print repr(Color.red) |
|
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 |
|
142 print repr(Color.red) |
|
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() |