PEP: 435 Title: Adding an Enum type to the Python standard library
Version: $Revision$ Last-Modified: $Date$ Author: Barry Warsaw
<barry@python.org>, Eli Bendersky <eliben@gmail.com>, Ethan Furman
<ethan@stoneleaf.us> Status: Final Type: Standards Track Content-Type:
text/x-rst Created: 23-Feb-2013 Python-Version: 3.4 Post-History:
23-Feb-2013, 02-May-2013 Replaces: 354 Resolution:
https://mail.python.org/pipermail/python-dev/2013-May/126112.html

Abstract

This PEP proposes adding an enumeration type to the Python standard
library.

An enumeration is a set of symbolic names bound to unique, constant
values. Within an enumeration, the values can be compared by identity,
and the enumeration itself can be iterated over.

Status of discussions

The idea of adding an enum type to Python is not new - PEP 354 is a
previous attempt that was rejected in 2005. Recently a new set of
discussions was initiated[1] on the python-ideas mailing list. Many new
ideas were proposed in several threads; after a lengthy discussion Guido
proposed adding flufl.enum to the standard library[2]. During the PyCon
2013 language summit the issue was discussed further. It became clear
that many developers want to see an enum that subclasses int, which can
allow us to replace many integer constants in the standard library by
enums with friendly string representations, without ceding backwards
compatibility. An additional discussion among several interested core
developers led to the proposal of having IntEnum as a special case of
Enum.

The key dividing issue between Enum and IntEnum is whether comparing to
integers is semantically meaningful. For most uses of enumerations, it's
a feature to reject comparison to integers; enums that compare to
integers lead, through transitivity, to comparisons between enums of
unrelated types, which isn't desirable in most cases. For some uses,
however, greater interoperability with integers is desired. For
instance, this is the case for replacing existing standard library
constants (such as socket.AF_INET) with enumerations.

Further discussion in late April 2013 led to the conclusion that
enumeration members should belong to the type of their enum:
type(Color.red) == Color. Guido has pronounced a decision on this
issue[3], as well as related issues of not allowing to subclass
enums[4], unless they define no enumeration members[5].

The PEP was accepted by Guido on May 10th, 2013[6].

Motivation

[Based partly on the Motivation stated in PEP 354*]*

The properties of an enumeration are useful for defining an immutable,
related set of constant values that may or may not have a semantic
meaning. Classic examples are days of the week (Sunday through Saturday)
and school assessment grades ('A' through 'D', and 'F'). Other examples
include error status values and states within a defined process.

It is possible to simply define a sequence of values of some other basic
type, such as int or str, to represent discrete arbitrary values.
However, an enumeration ensures that such values are distinct from any
others including, importantly, values within other enumerations, and
that operations without meaning ("Wednesday times two") are not defined
for these values. It also provides a convenient printable representation
of enum values without requiring tedious repetition while defining them
(i.e. no GREEN = 'green').

Module and type name

We propose to add a module named enum to the standard library. The main
type exposed by this module is Enum. Hence, to import the Enum type user
code will run:

    >>> from enum import Enum

Proposed semantics for the new enumeration type

Creating an Enum

Enumerations are created using the class syntax, which makes them easy
to read and write. An alternative creation method is described in
Functional API. To define an enumeration, subclass Enum as follows:

    >>> from enum import Enum
    >>> class Color(Enum):
    ...     red = 1
    ...     green = 2
    ...     blue = 3

A note on nomenclature: we call Color an enumeration (or enum) and
Color.red, Color.green are enumeration members (or enum members).
Enumeration members also have values (the value of Color.red is 1, etc.)

Enumeration members have human readable string representations:

    >>> print(Color.red)
    Color.red

...while their repr has more information:

    >>> print(repr(Color.red))
    <Color.red: 1>

The type of an enumeration member is the enumeration it belongs to:

    >>> type(Color.red)
    <Enum 'Color'>
    >>> isinstance(Color.green, Color)
    True
    >>>

Enums also have a property that contains just their item name:

    >>> print(Color.red.name)
    red

Enumerations support iteration, in definition order:

    >>> class Shake(Enum):
    ...   vanilla = 7
    ...   chocolate = 4
    ...   cookies = 9
    ...   mint = 3
    ...
    >>> for shake in Shake:
    ...   print(shake)
    ...
    Shake.vanilla
    Shake.chocolate
    Shake.cookies
    Shake.mint

Enumeration members are hashable, so they can be used in dictionaries
and sets:

    >>> apples = {}
    >>> apples[Color.red] = 'red delicious'
    >>> apples[Color.green] = 'granny smith'
    >>> apples
    {<Color.red: 1>: 'red delicious', <Color.green: 2>: 'granny smith'}

Programmatic access to enumeration members

Sometimes it's useful to access members in enumerations programmatically
(i.e. situations where Color.red won't do because the exact color is not
known at program-writing time). Enum allows such access:

    >>> Color(1)
    <Color.red: 1>
    >>> Color(3)
    <Color.blue: 3>

If you want to access enum members by name, use item access:

    >>> Color['red']
    <Color.red: 1>
    >>> Color['green']
    <Color.green: 2>

Duplicating enum members and values

Having two enum members with the same name is invalid:

    >>> class Shape(Enum):
    ...   square = 2
    ...   square = 3
    ...
    Traceback (most recent call last):
    ...
    TypeError: Attempted to reuse key: square

However, two enum members are allowed to have the same value. Given two
members A and B with the same value (and A defined first), B is an alias
to A. By-value lookup of the value of A and B will return A. By-name
lookup of B will also return A:

    >>> class Shape(Enum):
    ...   square = 2
    ...   diamond = 1
    ...   circle = 3
    ...   alias_for_square = 2
    ...
    >>> Shape.square
    <Shape.square: 2>
    >>> Shape.alias_for_square
    <Shape.square: 2>
    >>> Shape(2)
    <Shape.square: 2>

Iterating over the members of an enum does not provide the aliases:

    >>> list(Shape)
    [<Shape.square: 2>, <Shape.diamond: 1>, <Shape.circle: 3>]

The special attribute __members__ is an ordered dictionary mapping names
to members. It includes all names defined in the enumeration, including
the aliases:

    >>> for name, member in Shape.__members__.items():
    ...   name, member
    ...
    ('square', <Shape.square: 2>)
    ('diamond', <Shape.diamond: 1>)
    ('circle', <Shape.circle: 3>)
    ('alias_for_square', <Shape.square: 2>)

The __members__ attribute can be used for detailed programmatic access
to the enumeration members. For example, finding all the aliases:

    >>> [name for name, member in Shape.__members__.items() if member.name != name]
    ['alias_for_square']

Comparisons

Enumeration members are compared by identity:

    >>> Color.red is Color.red
    True
    >>> Color.red is Color.blue
    False
    >>> Color.red is not Color.blue
    True

Ordered comparisons between enumeration values are not supported. Enums
are not integers (but see IntEnum below):

    >>> Color.red < Color.blue
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unorderable types: Color() < Color()

Equality comparisons are defined though:

    >>> Color.blue == Color.red
    False
    >>> Color.blue != Color.red
    True
    >>> Color.blue == Color.blue
    True

Comparisons against non-enumeration values will always compare not equal
(again, IntEnum was explicitly designed to behave differently, see
below):

    >>> Color.blue == 2
    False

Allowed members and attributes of enumerations

The examples above use integers for enumeration values. Using integers
is short and handy (and provided by default by the Functional API), but
not strictly enforced. In the vast majority of use-cases, one doesn't
care what the actual value of an enumeration is. But if the value is
important, enumerations can have arbitrary values.

Enumerations are Python classes, and can have methods and special
methods as usual. If we have this enumeration:

    class Mood(Enum):
      funky = 1
      happy = 3

      def describe(self):
        # self is the member here
        return self.name, self.value

      def __str__(self):
        return 'my custom str! {0}'.format(self.value)

      @classmethod
      def favorite_mood(cls):
        # cls here is the enumeration
        return cls.happy

Then:

    >>> Mood.favorite_mood()
    <Mood.happy: 3>
    >>> Mood.happy.describe()
    ('happy', 3)
    >>> str(Mood.funky)
    'my custom str! 1'

The rules for what is allowed are as follows: all attributes defined
within an enumeration will become members of this enumeration, with the
exception of __dunder__ names and descriptors[7]; methods are
descriptors too.

Restricted subclassing of enumerations

Subclassing an enumeration is allowed only if the enumeration does not
define any members. So this is forbidden:

    >>> class MoreColor(Color):
    ...   pink = 17
    ...
    TypeError: Cannot extend enumerations

But this is allowed:

    >>> class Foo(Enum):
    ...   def some_behavior(self):
    ...     pass
    ...
    >>> class Bar(Foo):
    ...   happy = 1
    ...   sad = 2
    ...

The rationale for this decision was given by Guido in[8]. Allowing to
subclass enums that define members would lead to a violation of some
important invariants of types and instances. On the other hand, it makes
sense to allow sharing some common behavior between a group of
enumerations, and subclassing empty enumerations is also used to
implement IntEnum.

IntEnum

A variation of Enum is proposed which is also a subclass of int. Members
of an IntEnum can be compared to integers; by extension, integer
enumerations of different types can also be compared to each other:

    >>> from enum import IntEnum
    >>> class Shape(IntEnum):
    ...   circle = 1
    ...   square = 2
    ...
    >>> class Request(IntEnum):
    ...   post = 1
    ...   get = 2
    ...
    >>> Shape == 1
    False
    >>> Shape.circle == 1
    True
    >>> Shape.circle == Request.post
    True

However they still can't be compared to Enum:

    >>> class Shape(IntEnum):
    ...   circle = 1
    ...   square = 2
    ...
    >>> class Color(Enum):
    ...   red = 1
    ...   green = 2
    ...
    >>> Shape.circle == Color.red
    False

IntEnum values behave like integers in other ways you'd expect:

    >>> int(Shape.circle)
    1
    >>> ['a', 'b', 'c'][Shape.circle]
    'b'
    >>> [i for i in range(Shape.square)]
    [0, 1]

For the vast majority of code, Enum is strongly recommended, since
IntEnum breaks some semantic promises of an enumeration (by being
comparable to integers, and thus by transitivity to other unrelated
enumerations). It should be used only in special cases where there's no
other choice; for example, when integer constants are replaced with
enumerations and backwards compatibility is required with code that
still expects integers.

Other derived enumerations

IntEnum will be part of the enum module. However, it would be very
simple to implement independently:

    class IntEnum(int, Enum):
        pass

This demonstrates how similar derived enumerations can be defined, for
example a StrEnum that mixes in str instead of int.

Some rules:

1.  When subclassing Enum, mix-in types must appear before Enum itself
    in the sequence of bases, as in the IntEnum example above.
2.  While Enum can have members of any type, once you mix in an
    additional type, all the members must have values of that type, e.g.
    int above. This restriction does not apply to mix-ins which only add
    methods and don't specify another data type such as int or str.

Pickling

Enumerations can be pickled and unpickled:

    >>> from enum.tests.fruit import Fruit
    >>> from pickle import dumps, loads
    >>> Fruit.tomato is loads(dumps(Fruit.tomato))
    True

The usual restrictions for pickling apply: picklable enums must be
defined in the top level of a module, since unpickling requires them to
be importable from that module.

Functional API

The Enum class is callable, providing the following functional API:

    >>> Animal = Enum('Animal', 'ant bee cat dog')
    >>> Animal
    <Enum 'Animal'>
    >>> Animal.ant
    <Animal.ant: 1>
    >>> Animal.ant.value
    1
    >>> list(Animal)
    [<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]

The semantics of this API resemble namedtuple. The first argument of the
call to Enum is the name of the enumeration. Pickling enums created with
the functional API will work on CPython and PyPy, but for IronPython and
Jython you may need to specify the module name explicitly as follows:

    >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__)

The second argument is the source of enumeration member names. It can be
a whitespace-separated string of names, a sequence of names, a sequence
of 2-tuples with key/value pairs, or a mapping (e.g. dictionary) of
names to values. The last two options enable assigning arbitrary values
to enumerations; the others auto-assign increasing integers starting
with 1. A new class derived from Enum is returned. In other words, the
above assignment to Animal is equivalent to:

    >>> class Animals(Enum):
    ...   ant = 1
    ...   bee = 2
    ...   cat = 3
    ...   dog = 4

The reason for defaulting to 1 as the starting number and not 0 is that
0 is False in a boolean sense, but enum members all evaluate to True.

Proposed variations

Some variations were proposed during the discussions in the mailing
list. Here's some of the more popular ones.

flufl.enum

flufl.enum was the reference implementation upon which this PEP was
originally based. Eventually, it was decided against the inclusion of
flufl.enum because its design separated enumeration members from
enumerations, so the former are not instances of the latter. Its design
also explicitly permits subclassing enumerations for extending them with
more members (due to the member/enum separation, the type invariants are
not violated in flufl.enum with such a scheme).

Not having to specify values for enums

Michael Foord proposed (and Tim Delaney provided a proof-of-concept
implementation) to use metaclass magic that makes this possible:

    class Color(Enum):
        red, green, blue

The values get actually assigned only when first looked up.

Pros: cleaner syntax that requires less typing for a very common task
(just listing enumeration names without caring about the values).

Cons: involves much magic in the implementation, which makes even the
definition of such enums baffling when first seen. Besides, explicit is
better than implicit.

Using special names or forms to auto-assign enum values

A different approach to avoid specifying enum values is to use a special
name or form to auto assign them. For example:

    class Color(Enum):
        red = None          # auto-assigned to 0
        green = None        # auto-assigned to 1
        blue = None         # auto-assigned to 2

More flexibly:

    class Color(Enum):
        red = 7
        green = None        # auto-assigned to 8
        blue = 19
        purple = None       # auto-assigned to 20

Some variations on this theme:

1.  A special name auto imported from the enum package.
2.  Georg Brandl proposed ellipsis (...) instead of None to achieve the
    same effect.

Pros: no need to manually enter values. Makes it easier to change the
enum and extend it, especially for large enumerations.

Cons: actually longer to type in many simple cases. The argument of
explicit vs. implicit applies here as well.

Use-cases in the standard library

The Python standard library has many places where the usage of enums
would be beneficial to replace other idioms currently used to represent
them. Such usages can be divided to two categories: user-code facing
constants, and internal constants.

User-code facing constants like os.SEEK_*, socket module constants,
decimal rounding modes and HTML error codes could require backwards
compatibility since user code may expect integers. IntEnum as described
above provides the required semantics; being a subclass of int, it does
not affect user code that expects integers, while on the other hand
allowing printable representations for enumeration values:

    >>> import socket
    >>> family = socket.AF_INET
    >>> family == 2
    True
    >>> print(family)
    SocketFamily.AF_INET

Internal constants are not seen by user code but are employed internally
by stdlib modules. These can be implemented with Enum. Some examples
uncovered by a very partial skim through the stdlib: binhex, imaplib,
http/client, urllib/robotparser, idlelib, concurrent.futures,
turtledemo.

In addition, looking at the code of the Twisted library, there are many
use cases for replacing internal state constants with enums. The same
can be said about a lot of networking code (especially implementation of
protocols) and can be seen in test protocols written with the Tulip
library as well.

Acknowledgments

This PEP was initially proposing including the flufl.enum package[9] by
Barry Warsaw into the stdlib, and is inspired in large parts by it. Ben
Finney is the author of the earlier enumeration PEP 354.

References

Copyright

This document has been placed in the public domain.

[1] https://mail.python.org/pipermail/python-ideas/2013-January/019003.html

[2] https://mail.python.org/pipermail/python-ideas/2013-February/019373.html

[3] To make enums behave similarly to Python classes like bool, and
behave in a more intuitive way. It would be surprising if the type of
Color.red would not be Color. (Discussion in
https://mail.python.org/pipermail/python-dev/2013-April/125687.html)

[4] Subclassing enums and adding new members creates an unresolvable
situation; on one hand MoreColor.red and Color.red should not be the
same object, and on the other isinstance checks become confusing if they
are not. The discussion also links to Stack Overflow discussions that
make additional arguments.
(https://mail.python.org/pipermail/python-dev/2013-April/125716.html)

[5] It may be useful to have a class defining some behavior (methods,
with no actual enumeration members) mixed into an enum, and this would
not create the problem discussed in. (Discussion in
https://mail.python.org/pipermail/python-dev/2013-May/125859.html)

[6] https://mail.python.org/pipermail/python-dev/2013-May/126112.html

[7] http://docs.python.org/3/howto/descriptor.html

[8] Subclassing enums and adding new members creates an unresolvable
situation; on one hand MoreColor.red and Color.red should not be the
same object, and on the other isinstance checks become confusing if they
are not. The discussion also links to Stack Overflow discussions that
make additional arguments.
(https://mail.python.org/pipermail/python-dev/2013-April/125716.html)

[9] http://pythonhosted.org/flufl.enum/