blob: e1ae695e51d210ddc2887f978313cde5f188be47 [file] [log] [blame]
Guido van Rossum27e4aa31997-08-25 15:37:59 +00001"""Enumeration metaclass.
2
3XXX This is very much a work in progress.
4
5"""
Guido van Rossumbff110f1997-08-23 21:14:37 +00006
7import string
8
9class 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
90class 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(%s, %s, %s)" % (`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.
123Enum = EnumMetaClass("Enum", (), {})
124
125
126def _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
168if __name__ == '__main__':
169 _test()