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()
|
---|