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