|
1 """Enumeration metaclass. |
|
2 |
|
3 XXX This is very much a work in progress. |
|
4 |
|
5 """ |
|
6 |
|
7 import string |
|
8 |
|
9 class EnumMetaClass: |
|
10 """Metaclass for enumeration. |
|
11 |
|
12 To define your own enumeration, do something like |
|
13 |
|
14 class Color(Enum): |
|
15 red = 1 |
|
16 green = 2 |
|
17 blue = 3 |
|
18 |
|
19 Now, Color.red, Color.green and Color.blue behave totally |
|
20 different: they are enumerated values, not integers. |
|
21 |
|
22 Enumerations cannot be instantiated; however they can be |
|
23 subclassed. |
|
24 |
|
25 """ |
|
26 |
|
27 def __init__(self, name, bases, dict): |
|
28 """Constructor -- create an enumeration. |
|
29 |
|
30 Called at the end of the class statement. The arguments are |
|
31 the name of the new class, a tuple containing the base |
|
32 classes, and a dictionary containing everything that was |
|
33 entered in the class' namespace during execution of the class |
|
34 statement. In the above example, it would be {'red': 1, |
|
35 'green': 2, 'blue': 3}. |
|
36 |
|
37 """ |
|
38 for base in bases: |
|
39 if base.__class__ is not EnumMetaClass: |
|
40 raise TypeError, "Enumeration base class must be enumeration" |
|
41 bases = filter(lambda x: x is not Enum, bases) |
|
42 self.__name__ = name |
|
43 self.__bases__ = bases |
|
44 self.__dict = {} |
|
45 for key, value in dict.items(): |
|
46 self.__dict[key] = EnumInstance(name, key, value) |
|
47 |
|
48 def __getattr__(self, name): |
|
49 """Return an enumeration value. |
|
50 |
|
51 For example, Color.red returns the value corresponding to red. |
|
52 |
|
53 XXX Perhaps the values should be created in the constructor? |
|
54 |
|
55 This looks in the class dictionary and if it is not found |
|
56 there asks the base classes. |
|
57 |
|
58 The special attribute __members__ returns the list of names |
|
59 defined in this class (it does not merge in the names defined |
|
60 in base classes). |
|
61 |
|
62 """ |
|
63 if name == '__members__': |
|
64 return self.__dict.keys() |
|
65 |
|
66 try: |
|
67 return self.__dict[name] |
|
68 except KeyError: |
|
69 for base in self.__bases__: |
|
70 try: |
|
71 return getattr(base, name) |
|
72 except AttributeError: |
|
73 continue |
|
74 |
|
75 raise AttributeError, name |
|
76 |
|
77 def __repr__(self): |
|
78 s = self.__name__ |
|
79 if self.__bases__: |
|
80 s = s + '(' + string.join(map(lambda x: x.__name__, |
|
81 self.__bases__), ", ") + ')' |
|
82 if self.__dict: |
|
83 list = [] |
|
84 for key, value in self.__dict.items(): |
|
85 list.append("%s: %s" % (key, int(value))) |
|
86 s = "%s: {%s}" % (s, string.join(list, ", ")) |
|
87 return s |
|
88 |
|
89 |
|
90 class EnumInstance: |
|
91 """Class to represent an enumeration value. |
|
92 |
|
93 EnumInstance('Color', 'red', 12) prints as 'Color.red' and behaves |
|
94 like the integer 12 when compared, but doesn't support arithmetic. |
|
95 |
|
96 XXX Should it record the actual enumeration rather than just its |
|
97 name? |
|
98 |
|
99 """ |
|
100 |
|
101 def __init__(self, classname, enumname, value): |
|
102 self.__classname = classname |
|
103 self.__enumname = enumname |
|
104 self.__value = value |
|
105 |
|
106 def __int__(self): |
|
107 return self.__value |
|
108 |
|
109 def __repr__(self): |
|
110 return "EnumInstance(%r, %r, %r)" % (self.__classname, |
|
111 self.__enumname, |
|
112 self.__value) |
|
113 |
|
114 def __str__(self): |
|
115 return "%s.%s" % (self.__classname, self.__enumname) |
|
116 |
|
117 def __cmp__(self, other): |
|
118 return cmp(self.__value, int(other)) |
|
119 |
|
120 |
|
121 # Create the base class for enumerations. |
|
122 # It is an empty enumeration. |
|
123 Enum = EnumMetaClass("Enum", (), {}) |
|
124 |
|
125 |
|
126 def _test(): |
|
127 |
|
128 class Color(Enum): |
|
129 red = 1 |
|
130 green = 2 |
|
131 blue = 3 |
|
132 |
|
133 print Color.red |
|
134 print dir(Color) |
|
135 |
|
136 print Color.red == Color.red |
|
137 print Color.red == Color.blue |
|
138 print Color.red == 1 |
|
139 print Color.red == 2 |
|
140 |
|
141 class ExtendedColor(Color): |
|
142 white = 0 |
|
143 orange = 4 |
|
144 yellow = 5 |
|
145 purple = 6 |
|
146 black = 7 |
|
147 |
|
148 print ExtendedColor.orange |
|
149 print ExtendedColor.red |
|
150 |
|
151 print Color.red == ExtendedColor.red |
|
152 |
|
153 class OtherColor(Enum): |
|
154 white = 4 |
|
155 blue = 5 |
|
156 |
|
157 class MergedColor(Color, OtherColor): |
|
158 pass |
|
159 |
|
160 print MergedColor.red |
|
161 print MergedColor.white |
|
162 |
|
163 print Color |
|
164 print ExtendedColor |
|
165 print OtherColor |
|
166 print MergedColor |
|
167 |
|
168 if __name__ == '__main__': |
|
169 _test() |