blob: ae0498ca30e34f02669b33f2898c29ad4a7bbb02 [file] [log] [blame]
Ethan Furman6b3d64a2013-06-14 16:55:46 -07001:mod:`enum` --- Support for enumerations
2========================================
3
4.. module:: enum
Brett Cannon15e489f2013-06-14 21:59:16 -04005 :synopsis: Implementation of an enumeration class.
6
Ethan Furman6b3d64a2013-06-14 16:55:46 -07007.. :moduleauthor:: Ethan Furman <ethan@stoneleaf.us>
8.. :sectionauthor:: Barry Warsaw <barry@python.org>,
9.. :sectionauthor:: Eli Bendersky <eliben@gmail.com>,
10.. :sectionauthor:: Ethan Furman <ethan@stoneleaf.us>
11
12**Source code:** :source:`Lib/enum.py`
13
14----------------
15
16An enumeration is a set of symbolic names (members) bound to unique, constant
17values. Within an enumeration, the members can be compared by identity, and
18the enumeration itself can be iterated over.
19
20This module defines two enumeration classes that can be used to define unique
21sets of names and values: :class:`Enum` and :class:`IntEnum`.
22
23Creating an Enum
24----------------
25
26Enumerations are created using the :keyword:`class` syntax, which makes them
27easy to read and write. An alternative creation method is described in
28`Functional API`_. To define an enumeration, subclass :class:`Enum` as
29follows::
30
31 >>> from enum import Enum
32 >>> class Color(Enum):
33 ... red = 1
34 ... green = 2
35 ... blue = 3
36
37**A note on nomenclature**: we call :class:`Color` an *enumeration* (or *enum*)
38and :attr:`Color.red`, :attr:`Color.green` are *enumeration members* (or
39*enum members*). Enumeration members also have *values* (the value of
40:attr:`Color.red` is ``1``, etc.)
41
42Enumeration members have human readable string representations::
43
44 >>> print(Color.red)
45 Color.red
46
47...while their ``repr`` has more information::
48
49 >>> print(repr(Color.red))
50 <Color.red: 1>
51
52The *type* of an enumeration member is the enumeration it belongs to::
53
54 >>> type(Color.red)
55 <enum 'Color'>
56 >>> isinstance(Color.green, Color)
57 True
58 >>>
59
60Enum members also have a property that contains just their item name::
61
62 >>> print(Color.red.name)
63 red
64
65Enumerations support iteration, in definition order::
66
67 >>> class Shake(Enum):
68 ... vanilla = 7
69 ... chocolate = 4
70 ... cookies = 9
71 ... mint = 3
72 ...
73 >>> for shake in Shake:
74 ... print(shake)
75 ...
76 Shake.vanilla
77 Shake.chocolate
78 Shake.cookies
79 Shake.mint
80
81Enumeration members are hashable, so they can be used in dictionaries and sets::
82
83 >>> apples = {}
84 >>> apples[Color.red] = 'red delicious'
85 >>> apples[Color.green] = 'granny smith'
86 >>> apples == {Color.red: 'red delicious', Color.green: 'granny smith'}
87 True
88
89
90Programmatic access to enumeration members
91------------------------------------------
92
93Sometimes it's useful to access members in enumerations programmatically (i.e.
94situations where ``Color.red`` won't do because the exact color is not known
95at program-writing time). ``Enum`` allows such access::
96
97 >>> Color(1)
98 <Color.red: 1>
99 >>> Color(3)
100 <Color.blue: 3>
101
102If you want to access enum members by *name*, use item access::
103
104 >>> Color['red']
105 <Color.red: 1>
106 >>> Color['green']
107 <Color.green: 2>
108
109
110Duplicating enum members and values
111-----------------------------------
112
113Having two enum members with the same name is invalid::
114
115 >>> class Shape(Enum):
116 ... square = 2
117 ... square = 3
118 ...
119 Traceback (most recent call last):
120 ...
121 TypeError: Attempted to reuse key: 'square'
122
123However, two enum members are allowed to have the same value. Given two members
124A and B with the same value (and A defined first), B is an alias to A. By-value
125lookup of the value of A and B will return A. By-name lookup of B will also
126return A::
127
128 >>> class Shape(Enum):
129 ... square = 2
130 ... diamond = 1
131 ... circle = 3
132 ... alias_for_square = 2
133 ...
134 >>> Shape.square
135 <Shape.square: 2>
136 >>> Shape.alias_for_square
137 <Shape.square: 2>
138 >>> Shape(2)
139 <Shape.square: 2>
140
141Iterating over the members of an enum does not provide the aliases::
142
143 >>> list(Shape)
144 [<Shape.square: 2>, <Shape.diamond: 1>, <Shape.circle: 3>]
145
146The special attribute ``__members__`` is an ordered dictionary mapping names
147to members. It includes all names defined in the enumeration, including the
148aliases::
149
150 >>> for name, member in Shape.__members__.items():
151 ... name, member
152 ...
153 ('square', <Shape.square: 2>)
154 ('diamond', <Shape.diamond: 1>)
155 ('circle', <Shape.circle: 3>)
156 ('alias_for_square', <Shape.square: 2>)
157
158The ``__members__`` attribute can be used for detailed programmatic access to
159the enumeration members. For example, finding all the aliases::
160
161 >>> [name for name, member in Shape.__members__.items() if member.name != name]
162 ['alias_for_square']
163
164Comparisons
165-----------
166
167Enumeration members are compared by identity::
168
169 >>> Color.red is Color.red
170 True
171 >>> Color.red is Color.blue
172 False
173 >>> Color.red is not Color.blue
174 True
175
176Ordered comparisons between enumeration values are *not* supported. Enum
177members are not integers (but see `IntEnum`_ below)::
178
179 >>> Color.red < Color.blue
180 Traceback (most recent call last):
181 File "<stdin>", line 1, in <module>
182 TypeError: unorderable types: Color() < Color()
183
184Equality comparisons are defined though::
185
186 >>> Color.blue == Color.red
187 False
188 >>> Color.blue != Color.red
189 True
190 >>> Color.blue == Color.blue
191 True
192
193Comparisons against non-enumeration values will always compare not equal
194(again, class:`IntEnum` was explicitly designed to behave differently, see
195below)::
196
197 >>> Color.blue == 2
198 False
199
200
201Allowed members and attributes of enumerations
202----------------------------------------------
203
204The examples above use integers for enumeration values. Using integers is
205short and handy (and provided by default by the `Functional API`_), but not
206strictly enforced. In the vast majority of use-cases, one doesn't care what
207the actual value of an enumeration is. But if the value *is* important,
208enumerations can have arbitrary values.
209
210Enumerations are Python classes, and can have methods and special methods as
211usual. If we have this enumeration::
212
213 >>> class Mood(Enum):
214 ... funky = 1
215 ... happy = 3
216 ...
217 ... def describe(self):
218 ... # self is the member here
219 ... return self.name, self.value
220 ...
221 ... def __str__(self):
222 ... return 'my custom str! {0}'.format(self.value)
223 ...
224 ... @classmethod
225 ... def favorite_mood(cls):
226 ... # cls here is the enumeration
227 ... return cls.happy
228
229Then::
230
231 >>> Mood.favorite_mood()
232 <Mood.happy: 3>
233 >>> Mood.happy.describe()
234 ('happy', 3)
235 >>> str(Mood.funky)
236 'my custom str! 1'
237
238The rules for what is allowed are as follows: _sunder_ names (starting and
239ending with a single underscore) are reserved by enum and cannot be used;
240all other attributes defined within an enumeration will become members of this
241enumeration, with the exception of *__dunder__* names and descriptors (methods
242are also descriptors).
243
244Note: if your enumeration defines :meth:`__new__` and/or :meth:`__init__` then
245whatever value(s) were given to the enum member will be passed into those
246methods. See `Planet`_ for an example.
247
248
249Restricted subclassing of enumerations
250--------------------------------------
251
252Subclassing an enumeration is allowed only if the enumeration does not define
253any members. So this is forbidden::
254
255 >>> class MoreColor(Color):
256 ... pink = 17
257 Traceback (most recent call last):
258 ...
259 TypeError: Cannot extend enumerations
260
261But this is allowed::
262
263 >>> class Foo(Enum):
264 ... def some_behavior(self):
265 ... pass
266 ...
267 >>> class Bar(Foo):
268 ... happy = 1
269 ... sad = 2
270 ...
271
272Allowing subclassing of enums that define members would lead to a violation of
273some important invariants of types and instances. On the other hand, it makes
274sense to allow sharing some common behavior between a group of enumerations.
275(See `OrderedEnum`_ for an example.)
276
277
278Pickling
279--------
280
281Enumerations can be pickled and unpickled::
282
283 >>> from test.test_enum import Fruit
284 >>> from pickle import dumps, loads
285 >>> Fruit.tomato is loads(dumps(Fruit.tomato))
286 True
287
288The usual restrictions for pickling apply: picklable enums must be defined in
289the top level of a module, since unpickling requires them to be importable
290from that module.
291
292.. warning::
293
294 In order to support the singleton nature of enumeration members, pickle
295 protocol version 2 or higher must be used.
296
297
298Functional API
299--------------
300
301The :class:`Enum` class is callable, providing the following functional API::
302
303 >>> Animal = Enum('Animal', 'ant bee cat dog')
304 >>> Animal
305 <enum 'Animal'>
306 >>> Animal.ant
307 <Animal.ant: 1>
308 >>> Animal.ant.value
309 1
310 >>> list(Animal)
311 [<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]
312
313The semantics of this API resemble :class:`namedtuple`. The first argument
314of the call to :class:`Enum` is the name of the enumeration.
315
316The second argument is the *source* of enumeration member names. It can be a
317whitespace-separated string of names, a sequence of names, a sequence of
3182-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to
319values. The last two options enable assigning arbitrary values to
320enumerations; the others auto-assign increasing integers starting with 1. A
321new class derived from :class:`Enum` is returned. In other words, the above
322assignment to :class:`Animal` is equivalent to::
323
324 >>> class Animals(Enum):
325 ... ant = 1
326 ... bee = 2
327 ... cat = 3
328 ... dog = 4
329
330Pickling enums created with the functional API can be tricky as frame stack
331implementation details are used to try and figure out which module the
332enumeration is being created in (e.g. it will fail if you use a utility
333function in separate module, and also may not work on IronPython or Jython).
334The solution is to specify the module name explicitly as follows::
335
336 >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__)
337
338Derived Enumerations
339====================
340
341IntEnum
342-------
343
344A variation of :class:`Enum` is provided which is also a subclass of
345:class:`int`. Members of an :class:`IntEnum` can be compared to integers;
346by extension, integer enumerations of different types can also be compared
347to each other::
348
349 >>> from enum import IntEnum
350 >>> class Shape(IntEnum):
351 ... circle = 1
352 ... square = 2
353 ...
354 >>> class Request(IntEnum):
355 ... post = 1
356 ... get = 2
357 ...
358 >>> Shape == 1
359 False
360 >>> Shape.circle == 1
361 True
362 >>> Shape.circle == Request.post
363 True
364
365However, they still can't be compared to standard :class:`Enum` enumerations::
366
367 >>> class Shape(IntEnum):
368 ... circle = 1
369 ... square = 2
370 ...
371 >>> class Color(Enum):
372 ... red = 1
373 ... green = 2
374 ...
375 >>> Shape.circle == Color.red
376 False
377
378:class:`IntEnum` values behave like integers in other ways you'd expect::
379
380 >>> int(Shape.circle)
381 1
382 >>> ['a', 'b', 'c'][Shape.circle]
383 'b'
384 >>> [i for i in range(Shape.square)]
385 [0, 1]
386
387For the vast majority of code, :class:`Enum` is strongly recommended,
388since :class:`IntEnum` breaks some semantic promises of an enumeration (by
389being comparable to integers, and thus by transitivity to other
390unrelated enumerations). It should be used only in special cases where
391there's no other choice; for example, when integer constants are
392replaced with enumerations and backwards compatibility is required with code
393that still expects integers.
394
395
396Others
397------
398
399While :class:`IntEnum` is part of the :mod:`enum` module, it would be very
400simple to implement independently::
401
402 class IntEnum(int, Enum):
403 pass
404
405This demonstrates how similar derived enumerations can be defined; for example
406a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`.
407
408Some rules:
409
4101. When subclassing :class:`Enum`, mix-in types must appear before
411 :class:`Enum` itself in the sequence of bases, as in the :class:`IntEnum`
412 example above.
4132. While :class:`Enum` can have members of any type, once you mix in an
414 additional type, all the members must have values of that type, e.g.
415 :class:`int` above. This restriction does not apply to mix-ins which only
416 add methods and don't specify another data type such as :class:`int` or
417 :class:`str`.
4183. When another data type is mixed in, the :attr:`value` attribute is *not the
419 same* as the enum member itself, although it is equivalant and will compare
420 equal.
421
422
423Interesting examples
424====================
425
426While :class:`Enum` and :class:`IntEnum` are expected to cover the majority of
427use-cases, they cannot cover them all. Here are recipes for some different
428types of enumerations that can be used directly, or as examples for creating
429one's own.
430
431
432AutoNumber
433----------
434
435Avoids having to specify the value for each enumeration member::
436
437 >>> class AutoNumber(Enum):
438 ... def __new__(cls):
439 ... value = len(cls.__members__) + 1
440 ... obj = object.__new__(cls)
441 ... obj._value = value
442 ... return obj
443 ...
444 >>> class Color(AutoNumber):
445 ... red = ()
446 ... green = ()
447 ... blue = ()
448 ...
449 >>> Color.green.value == 2
450 True
451
452
453UniqueEnum
454----------
455
456Raises an error if a duplicate member name is found instead of creating an
457alias::
458
459 >>> class UniqueEnum(Enum):
460 ... def __init__(self, *args):
461 ... cls = self.__class__
462 ... if any(self.value == e.value for e in cls):
463 ... a = self.name
464 ... e = cls(self.value).name
465 ... raise ValueError(
466 ... "aliases not allowed in UniqueEnum: %r --> %r"
467 ... % (a, e))
468 ...
469 >>> class Color(UniqueEnum):
470 ... red = 1
471 ... green = 2
472 ... blue = 3
473 ... grene = 2
474 Traceback (most recent call last):
475 ...
476 ValueError: aliases not allowed in UniqueEnum: 'grene' --> 'green'
477
478
479OrderedEnum
480-----------
481
482An ordered enumeration that is not based on :class:`IntEnum` and so maintains
483the normal :class:`Enum` invariants (such as not being comparable to other
484enumerations)::
485
486 >>> class OrderedEnum(Enum):
487 ... def __ge__(self, other):
488 ... if self.__class__ is other.__class__:
489 ... return self._value >= other._value
490 ... return NotImplemented
491 ... def __gt__(self, other):
492 ... if self.__class__ is other.__class__:
493 ... return self._value > other._value
494 ... return NotImplemented
495 ... def __le__(self, other):
496 ... if self.__class__ is other.__class__:
497 ... return self._value <= other._value
498 ... return NotImplemented
499 ... def __lt__(self, other):
500 ... if self.__class__ is other.__class__:
501 ... return self._value < other._value
502 ... return NotImplemented
503 ...
504 >>> class Grade(OrderedEnum):
505 ... A = 5
506 ... B = 4
507 ... C = 3
508 ... D = 2
509 ... F = 1
510 ...
511 >>> Grade.C < Grade.A
512 True
513
514
515Planet
516------
517
518If :meth:`__new__` or :meth:`__init__` is defined the value of the enum member
519will be passed to those methods::
520
521 >>> class Planet(Enum):
522 ... MERCURY = (3.303e+23, 2.4397e6)
523 ... VENUS = (4.869e+24, 6.0518e6)
524 ... EARTH = (5.976e+24, 6.37814e6)
525 ... MARS = (6.421e+23, 3.3972e6)
526 ... JUPITER = (1.9e+27, 7.1492e7)
527 ... SATURN = (5.688e+26, 6.0268e7)
528 ... URANUS = (8.686e+25, 2.5559e7)
529 ... NEPTUNE = (1.024e+26, 2.4746e7)
530 ... def __init__(self, mass, radius):
531 ... self.mass = mass # in kilograms
532 ... self.radius = radius # in meters
533 ... @property
534 ... def surface_gravity(self):
535 ... # universal gravitational constant (m3 kg-1 s-2)
536 ... G = 6.67300E-11
537 ... return G * self.mass / (self.radius * self.radius)
538 ...
539 >>> Planet.EARTH.value
540 (5.976e+24, 6378140.0)
541 >>> Planet.EARTH.surface_gravity
542 9.802652743337129