blob: 13a3ed7d6dd73d0691f0884d639731fae39afa14 [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):
Guido van Rossum4117e541998-09-14 16:44:15 +000015 red = 1
16 green = 2
17 blue = 3
Guido van Rossumbff110f1997-08-23 21:14:37 +000018
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):
Guido van Rossum4117e541998-09-14 16:44:15 +000028 """Constructor -- create an enumeration.
Guido van Rossumbff110f1997-08-23 21:14:37 +000029
Guido van Rossum4117e541998-09-14 16:44:15 +000030 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}.
Guido van Rossumbff110f1997-08-23 21:14:37 +000036
Guido van Rossum4117e541998-09-14 16:44:15 +000037 """
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)
Guido van Rossumbff110f1997-08-23 21:14:37 +000047
48 def __getattr__(self, name):
Guido van Rossum4117e541998-09-14 16:44:15 +000049 """Return an enumeration value.
Guido van Rossumbff110f1997-08-23 21:14:37 +000050
Guido van Rossum4117e541998-09-14 16:44:15 +000051 For example, Color.red returns the value corresponding to red.
Guido van Rossumbff110f1997-08-23 21:14:37 +000052
Guido van Rossum4117e541998-09-14 16:44:15 +000053 XXX Perhaps the values should be created in the constructor?
Guido van Rossumbff110f1997-08-23 21:14:37 +000054
Guido van Rossum4117e541998-09-14 16:44:15 +000055 This looks in the class dictionary and if it is not found
56 there asks the base classes.
Guido van Rossumbff110f1997-08-23 21:14:37 +000057
Guido van Rossum4117e541998-09-14 16:44:15 +000058 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).
Guido van Rossumbff110f1997-08-23 21:14:37 +000061
Guido van Rossum4117e541998-09-14 16:44:15 +000062 """
63 if name == '__members__':
64 return self.__dict.keys()
Guido van Rossumbff110f1997-08-23 21:14:37 +000065
Guido van Rossum4117e541998-09-14 16:44:15 +000066 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
Guido van Rossumbff110f1997-08-23 21:14:37 +000074
Guido van Rossum4117e541998-09-14 16:44:15 +000075 raise AttributeError, name
Guido van Rossumbff110f1997-08-23 21:14:37 +000076
77 def __repr__(self):
Guido van Rossum4117e541998-09-14 16:44:15 +000078 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
Guido van Rossumbff110f1997-08-23 21:14:37 +000088
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):
Guido van Rossum4117e541998-09-14 16:44:15 +0000102 self.__classname = classname
103 self.__enumname = enumname
104 self.__value = value
Guido van Rossumbff110f1997-08-23 21:14:37 +0000105
106 def __int__(self):
Guido van Rossum4117e541998-09-14 16:44:15 +0000107 return self.__value
Guido van Rossumbff110f1997-08-23 21:14:37 +0000108
109 def __repr__(self):
Guido van Rossum4117e541998-09-14 16:44:15 +0000110 return "EnumInstance(%s, %s, %s)" % (`self.__classname`,
111 `self.__enumname`,
112 `self.__value`)
Guido van Rossumbff110f1997-08-23 21:14:37 +0000113
114 def __str__(self):
Guido van Rossum4117e541998-09-14 16:44:15 +0000115 return "%s.%s" % (self.__classname, self.__enumname)
Guido van Rossumbff110f1997-08-23 21:14:37 +0000116
117 def __cmp__(self, other):
Guido van Rossum4117e541998-09-14 16:44:15 +0000118 return cmp(self.__value, int(other))
Guido van Rossumbff110f1997-08-23 21:14:37 +0000119
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):
Guido van Rossum4117e541998-09-14 16:44:15 +0000129 red = 1
130 green = 2
131 blue = 3
Guido van Rossumbff110f1997-08-23 21:14:37 +0000132
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):
Guido van Rossum4117e541998-09-14 16:44:15 +0000142 white = 0
143 orange = 4
144 yellow = 5
145 purple = 6
146 black = 7
Guido van Rossumbff110f1997-08-23 21:14:37 +0000147
148 print ExtendedColor.orange
149 print ExtendedColor.red
150
151 print Color.red == ExtendedColor.red
152
153 class OtherColor(Enum):
Guido van Rossum4117e541998-09-14 16:44:15 +0000154 white = 4
155 blue = 5
Guido van Rossumbff110f1997-08-23 21:14:37 +0000156
157 class MergedColor(Color, OtherColor):
Guido van Rossum4117e541998-09-14 16:44:15 +0000158 pass
Guido van Rossumbff110f1997-08-23 21:14:37 +0000159
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()