<div dir="ltr"><div><div><div><div><div>Hello python-dev,<br><br></div>We're happy to present the revised PEP 435, collecting valuable feedback from python-ideas discussions as well as in-person discussions and decisions made during the latest PyCon language summit. We believe the proposal is now better than the original one, providing both a wider set of features and more convenient ways to use those features.<br>

<br></div>Link to the PEP: <a href="http://www.python.org/dev/peps/pep-0435/">http://www.python.org/dev/peps/pep-0435/</a> [it's also pasted fully below for convenience].<br><br></div>Reference implementation is available as the recently released flufl.enum version 4.0 - you can get it either from PyPi or <a href="https://launchpad.net/flufl.enum">https://launchpad.net/flufl.enum</a>. flufl.enum 4.0 was developed in parallel with revising PEP 435.<br>

<br></div>Comments welcome,<br><br></div>Barry and Eli<br><br>----------------------------------<br><br>PEP: 435<br>Title: Adding an Enum type to the Python standard library<br>Version: $Revision$<br>Last-Modified: $Date$<br>

Author: Barry Warsaw <<a href="mailto:barry@python.org">barry@python.org</a>>,<br>        Eli Bendersky <<a href="mailto:eliben@gmail.com">eliben@gmail.com</a>><br>Status: Draft<br>Type: Standards Track<br>Content-Type: text/x-rst<br>

Created: 2013-02-23<br>Python-Version: 3.4<br>Post-History: 2013-02-23<br><br><br>Abstract<br>========<br><br>This PEP proposes adding an enumeration type to the Python standard library.<br>Specifically, it proposes moving the existing ``flufl.enum`` package by Barry<br>

Warsaw into the standard library.  Much of this PEP is based on the "using"<br>[1]_ document from the documentation of ``flufl.enum``.<br><br>An enumeration is a set of symbolic names bound to unique, constant values.<br>

Within an enumeration, the values can be compared by identity, and the<br>enumeration itself can be iterated over.<br><br><br>Decision<br>========<br><br>TODO: update decision here once pronouncement is made.<br><br><br>
Status of discussions<br>
=====================<br><br>The idea of adding an enum type to Python is not new - PEP 354 [2]_ is a<br>previous attempt that was rejected in 2005.  Recently a new set of discussions<br>was initiated [3]_ on the ``python-ideas`` mailing list.  Many new ideas were<br>

proposed in several threads; after a lengthy discussion Guido proposed adding<br>``flufl.enum`` to the standard library [4]_.  During the PyCon 2013 language<br>summit the issue was discussed further.  It became clear that many developers<br>

want to see an enum that subclasses ``int``, which can allow us to replace<br>many integer constants in the standard library by enums with friendly string<br>representations, without ceding backwards compatibility.  An additional<br>

discussion among several interested core developers led to the proposal of<br>having ``IntEnum`` as a special case of ``Enum``.<br><br>The key dividing issue between ``Enum`` and ``IntEnum`` is whether comparing<br>to integers is semantically meaningful.  For most uses of enumerations, it's<br>

a **feature** to reject comparison to integers; enums that compare to integers<br>lead, through transitivity, to comparisons between enums of unrelated types,<br>which isn't desirable in most cases.  For some uses, however, greater<br>

interoperatiliby with integers is desired. For instance, this is the case for<br>replacing existing standard library constants (such as ``socket.AF_INET``)<br>with enumerations.<br><br>This PEP is an attempt to formalize this decision as well as discuss a number<br>

of variations that were discussed and can be considered for inclusion.<br><br><br>Motivation<br>==========<br><br>*[Based partly on the Motivation stated in PEP 354]*<br><br>The properties of an enumeration are useful for defining an immutable, related<br>

set of constant values that have a defined sequence but no inherent semantic<br>meaning.  Classic examples are days of the week (Sunday through Saturday) and<br>school assessment grades ('A' through 'D', and 'F').  Other examples include<br>

error status values and states within a defined process.<br><br>It is possible to simply define a sequence of values of some other basic type,<br>such as ``int`` or ``str``, to represent discrete arbitrary values.  However,<br>

an enumeration ensures that such values are distinct from any others including,<br>importantly, values within other enumerations, and that operations without<br>meaning ("Wednesday times two") are not defined for these values.  It also<br>

provides a convenient printable representation of enum values without requiring<br>tedious repetition while defining them (i.e. no ``GREEN = 'green'``).<br><br><br>Module and type name<br>====================<br>
<br>
We propose to add a module named ``enum`` to the standard library.  The main<br>type exposed by this module is ``Enum``.  Hence, to import the ``Enum`` type<br>user code will run::<br><br>    >>> from enum import Enum<br>

<br><br>Proposed semantics for the new enumeration type<br>===============================================<br><br>Creating an Enum<br>----------------<br><br>Enumerations are created using the class syntax, which makes them easy to read<br>

and write.  An alternative creation method is described in `Convenience API`_.<br>To define an enumeration, derive from the ``Enum`` class and add attributes<br>with assignment to their integer values::<br><br>    >>> from enum import Enum<br>

    >>> class Colors(Enum):<br>    ...     red = 1<br>    ...     green = 2<br>    ...     blue = 3<br><br>Enumeration values have nice, human readable string representations::<br><br>    >>> print(Colors.red)<br>

    Colors.red<br><br>...while their repr has more information::<br><br>    >>> print(repr(Colors.red))<br>    <EnumValue: Colors.red [value=1]><br><br>The enumeration value names are available through the class members::<br>

<br>    >>> for member in Colors.__members__:<br>    ...     print(member)<br>    red<br>    green<br>    blue<br><br>Let's say you wanted to encode an enumeration value in a database.  You might<br>want to get the enumeration class object from an enumeration value::<br>

<br>    >>> cls = Colors.red.enum<br>    >>> print(cls.__name__)<br>    Colors<br><br>Enums also have a property that contains just their item name::<br><br>    >>> print(<a href="http://Colors.red.name">Colors.red.name</a>)<br>

    red<br>    >>> print(<a href="http://Colors.green.name">Colors.green.name</a>)<br>    green<br>    >>> print(<a href="http://Colors.blue.name">Colors.blue.name</a>)<br>    blue<br><br>The str and repr of the enumeration class also provides useful information::<br>

<br>    >>> print(Colors)<br>    <Colors {red: 1, green: 2, blue: 3}><br>    >>> print(repr(Colors))<br>    <Colors {red: 1, green: 2, blue: 3}><br><br>The ``Enum`` class supports iteration.  Iteration is defined as the<br>

sorted order of the item values::<br><br>    >>> class FiveColors(Enum):<br>    ...     pink = 4<br>    ...     cyan = 5<br>    ...     green = 2<br>    ...     blue = 3<br>    ...     red = 1<br>    >>> [<a href="http://v.name">v.name</a> for v in FiveColors]<br>

    ['red', 'green', 'blue', 'pink', 'cyan']<br><br>Enumeration values are hashable, so they can be used in dictionaries and sets::<br><br>    >>> apples = {}<br>    >>> apples[Colors.red] = 'red delicious'<br>

    >>> apples[Colors.green] = 'granny smith'<br>    >>> apples<br>    {<EnumValue: Colors.green [value=2]>: 'granny smith', <EnumValue: Colors.red [value=1]>: 'red delicious'}<br>

<br>To programmatically access enumeration values, use ``getattr``::<br><br>    >>> getattr(Colors, 'red')<br>    <EnumValue: Colors.red [value=1]><br><br>Comparisons<br>-----------<br><br>Enumeration values are compared by identity::<br>

<br>    >>> Colors.red is Colors.red<br>    True<br>    >>> Colors.blue is Colors.blue<br>    True<br>    >>> Colors.red is not Colors.blue<br>    True<br>    >>> Colors.blue is Colors.red<br>

    False<br><br>Ordered comparisons between enumeration values are *not* supported.  Enums are<br>not integers (but see `IntEnum`_ below)::<br><br>    >>> Colors.red < Colors.blue<br>    Traceback (most recent call last):<br>

    ...<br>    NotImplementedError<br>    >>> Colors.red <= Colors.blue<br>    Traceback (most recent call last):<br>    ...<br>    NotImplementedError<br>    >>> Colors.blue > Colors.green<br>    Traceback (most recent call last):<br>

    ...<br>    NotImplementedError<br>    >>> Colors.blue >= Colors.green<br>    Traceback (most recent call last):<br>    ...<br>    NotImplementedError<br><br>Equality comparisons are defined though::<br><br>

    >>> Colors.blue == Colors.blue<br>    True<br>    >>> Colors.green != Colors.blue<br>    True<br><br>Comparisons against non-enumeration values will always compare not equal::<br><br>    >>> Colors.green == 2<br>

    False<br>    >>> Colors.blue == 3<br>    False<br>    >>> Colors.green != 3<br>    True<br>    >>> Colors.green == 'green'<br>    False<br><br><br>Extending enumerations by subclassing<br>

-------------------------------------<br><br>You can extend previously defined Enums by subclassing::<br><br>    >>> class MoreColors(Colors):<br>    ...     pink = 4<br>    ...     cyan = 5<br><br>When extended in this way, the base enumeration's values are identical to the<br>

same named values in the derived class::<br><br>    >>> Colors.red is MoreColors.red<br>    True<br>    >>> Colors.blue is MoreColors.blue<br>    True<br><br>However, these are not doing comparisons against the integer<br>

equivalent values, because if you define an enumeration with similar<br>item names and integer values, they will not be identical::<br><br>    >>> class OtherColors(Enum):<br>    ...     red = 1<br>    ...     blue = 2<br>

    ...     yellow = 3<br>    >>> Colors.red is OtherColors.red<br>    False<br>    >>> Colors.blue is not OtherColors.blue<br>    True<br><br>These enumeration values are not equal, nor do they and hence may exist<br>

in the same set, or as distinct keys in the same dictionary::<br><br>    >>> Colors.red == OtherColors.red<br>    False<br>    >>> len(set((Colors.red, OtherColors.red)))<br>    2<br><br>You may not define two enumeration values with the same integer value::<br>

<br>    >>> class Bad(Enum):<br>    ...     cartman = 1<br>    ...     stan = 2<br>    ...     kyle = 3<br>    ...     kenny = 3 # Oops!<br>    ...     butters = 4<br>    Traceback (most recent call last):<br>    ...<br>

    ValueError: Conflicting enums with value '3': 'kenny' and 'kyle'<br><br>You also may not duplicate values in derived enumerations::<br><br>    >>> class BadColors(Colors):<br>    ...     yellow = 4<br>

    ...     chartreuse = 2 # Oops!<br>    Traceback (most recent call last):<br>    ...<br>    ValueError: Conflicting enums with value '2': 'green' and 'chartreuse'<br><br><br>Enumeration values<br>

------------------<br><br>The examples above use integers for enumeration values.  Using integers is<br>short and handy (and provided by default by the `Convenience API`_), but not<br>strictly enforced.  In the vast majority of use-cases, one doesn't care what<br>

the actual value of an enumeration is.  But if the value *is* important,<br>enumerations can have arbitrary values.  The following example uses strings::<br><br>    >>> class SpecialId(Enum):<br>    ...   selector = '$IM($N)'<br>

    ...   adaptor = '~$IM'<br>    ...<br>    >>> SpecialId.selector<br>    <EnumValue: SpecialId.selector [value=$IM($N)]><br>    >>> SpecialId.selector.value<br>    '$IM($N)'<br>    >>> a = SpecialId.adaptor<br>

    >>> a == '~$IM'<br>    False<br>    >>> a == SpecialId.adaptor<br>    True<br>    >>> print(a)<br>    SpecialId.adaptor<br>    >>> print(a.value)<br>    ~$IM<br><br>Here ``Enum`` is used to provide readable (and syntactically valid!) names for<br>

some special values, as well as group them together.<br><br>While ``Enum`` supports this flexibility, one should only use it in<br>very special cases.  Code will be most readable when actual values of<br>enumerations aren't important and enumerations are just used for their<br>

naming and comparison properties.<br><br><br>IntEnum<br>-------<br><br>A variation of ``Enum`` is proposed where the enumeration values also<br>subclasses ``int`` - ``IntEnum``.  These values can be compared to<br>integers; by extension, enumerations of different types can also be<br>

compared to each other::<br><br>    >>> from enum import IntEnum<br>    >>> class Shape(IntEnum):<br>    ...   circle = 1<br>    ...   square = 2<br>    ...<br>    >>> class Request(IntEnum):<br>

    ...   post = 1<br>    ...   get = 2<br>    ...<br>    >>> Shape == 1<br>    False<br>    >>> Shape.circle == 1<br>    True<br>    >>> Shape.circle == Request.post<br>    True<br><br>However they still can't be compared to ``Enum``::<br>

<br>    >>> class Shape(IntEnum):<br>    ...   circle = 1<br>    ...   square = 2<br>    ...<br>    >>> class Colors(Enum):<br>    ...   red = 1<br>    ...   green = 2<br>    ...<br>    >>> Shape.circle == Colors.red<br>

    False<br><br>``IntEnum`` values behave like integers in other ways you'd expect::<br><br>    >>> int(Shape.circle)<br>    1<br>    >>> ['a', 'b', 'c'][Shape.circle]<br>    'b'<br>

    >>> [i for i in range(Shape.square)]<br>    [0, 1]<br><br>For the vast majority of code, ``Enum`` is strongly recommended.<br>Since ``IntEnum`` breaks some semantic promises of an enumeration (by<br>being comparable to integers, and thus by transitivity to other<br>

unrelated enumerations), it should be used only in special cases where<br>there's no other choice; for example, when integer constants are<br>replaced with enumerations and backwards compatibility is required<br>with code that still expects integers.<br>

<br><br>Pickling<br>--------<br><br>Enumerations created with the class syntax can also be pickled and unpickled::<br><br>    >>> from enum.tests.fruit import Fruit<br>    >>> from pickle import dumps, loads<br>

    >>> Fruit.tomato is loads(dumps(Fruit.tomato))<br>    True<br><br><br>Convenience API<br>---------------<br><br>The ``Enum`` class is callable, providing the following convenience API::<br><br>    >>> Animals = Enum('Animals', 'ant bee cat dog')<br>

    >>> Animals<br>    <Animals {ant: 1, bee: 2, cat: 3, dog: 4}><br>    >>> Animals.ant<br>    <EnumValue: Animals.ant [value=1]><br>    >>> Animals.ant.value<br>    1<br><br>The semantics of this API resemble ``namedtuple``. The first argument of<br>

the call to ``Enum`` is the name of the enumeration.  The second argument is<br>a source of enumeration value names.  It can be a whitespace-separated string<br>of names, a sequence of names or a sequence of 2-tuples with key/value pairs.<br>

The last option enables assigning arbitrary values to enumerations; the others<br>auto-assign increasing integers starting with 1.  A new class derived from<br>``Enum`` is returned.  In other words, the above assignment to ``Animals`` is<br>

equivalent to::<br><br>    >>> class Animals(Enum):<br>    ...   ant = 1<br>    ...   bee = 2<br>    ...   cat = 3<br>    ...   dog = 4<br><br>Examples of alternative name/value specifications::<br><br>    >>> Enum('Animals', ['ant', 'bee', 'cat', 'dog'])<br>

    <Animals {ant: 1, bee: 2, cat: 3, dog: 4}><br>    >>> Enum('Animals', (('ant', 'one'), ('bee', 'two'), ('cat', 'three'), ('dog', 'four')))<br>

    <Animals {dog: four, ant: one, cat: three, bee: two}><br><br>The second argument can also be a dictionary mapping names to values::<br><br>    >>> levels = dict(debug=10, info=20, warning=30, severe=40)<br>

    >>> Enum('Levels', levels)<br>    <Levels {debug: 10, info: 20, warning: 30, severe: 40}><br><br><br>Proposed variations<br>===================<br><br>Some variations were proposed during the discussions in the mailing list.<br>

Here's some of the more popular ones.<br><br><br>Not having to specify values for enums<br>--------------------------------------<br><br>Michael Foord proposed (and Tim Delaney provided a proof-of-concept<br>implementation) to use metaclass magic that makes this possible::<br>

<br>    class Color(Enum):<br>        red, green, blue<br><br>The values get actually assigned only when first looked up.<br><br>Pros: cleaner syntax that requires less typing for a very common task (just<br>listing enumeration names without caring about the values).<br>

<br>Cons: involves much magic in the implementation, which makes even the<br>definition of such enums baffling when first seen.  Besides, explicit is<br>better than implicit.<br><br><br>Using special names or forms to auto-assign enum values<br>

-------------------------------------------------------<br><br>A different approach to avoid specifying enum values is to use a special name<br>or form to auto assign them.  For example::<br><br>    class Color(Enum):<br>

        red = None          # auto-assigned to 0<br>        green = None        # auto-assigned to 1<br>        blue = None         # auto-assigned to 2<br><br>More flexibly::<br><br>    class Color(Enum):<br>        red = 7<br>

        green = None        # auto-assigned to 8<br>        blue = 19<br>        purple = None       # auto-assigned to 20<br><br>Some variations on this theme:<br><br>#. A special name ``auto`` imported from the enum package.<br>

#. Georg Brandl proposed ellipsis (``...``) instead of ``None`` to achieve the<br>   same effect.<br><br>Pros: no need to manually enter values. Makes it easier to change the enum and<br>extend it, especially for large enumerations.<br>

<br>Cons: actually longer to type in many simple cases.  The argument of explicit<br>vs. implicit applies here as well.<br><br><br>Use-cases in the standard library<br>=================================<br><br>The Python standard library has many places where the usage of enums would be<br>

beneficial to replace other idioms currently used to represent them.  Such<br>usages can be divided to two categories: user-code facing constants, and<br>internal constants.<br><br>User-code facing constants like ``os.SEEK_*``, ``socket`` module constants,<br>

decimal rounding modes and HTML error codes could require backwards<br>compatibility since user code may expect integers.  ``IntEnum`` as described<br>above provides the required semantics; being a subclass of ``int``, it does not<br>

affect user code that expects integers, while on the other hand allowing<br>printable representations for enumeration values::<br><br>    >>> import socket<br>    >>> family = socket.AF_INET<br>    >>> family == 2<br>

    True<br>    >>> print(family)<br>    SocketFamily.AF_INET<br><br>Internal constants are not seen by user code but are employed internally by<br>stdlib modules.  These can be implemented with ``Enum``.  Some examples<br>

uncovered by a very partial skim through the stdlib: ``binhex``, ``imaplib``,<br>``http/client``, ``urllib/robotparser``, ``idlelib``, ``concurrent.futures``,<br>``turtledemo``.<br><br>In addition, looking at the code of the Twisted library, there are many use<br>

cases for replacing internal state constants with enums.  The same can be said<br>about a lot of networking code (especially implementation of protocols) and<br>can be seen in test protocols written with the Tulip library as well.<br>

<br><br>Differences from PEP 354<br>========================<br><br>Unlike PEP 354, enumeration values are not defined as a sequence of strings,<br>but as attributes of a class.  This design was chosen because it was felt that<br>

class syntax is more readable.<br><br>Unlike PEP 354, enumeration values require an explicit integer value.  This<br>difference recognizes that enumerations often represent real-world values, or<br>must interoperate with external real-world systems.  For example, to store an<br>

enumeration in a database, it is better to convert it to an integer on the way<br>in and back to an enumeration on the way out.  Providing an integer value also<br>provides an explicit ordering.  However, there is no automatic conversion to<br>

and from the integer values, because explicit is better than implicit.<br><br>Unlike PEP 354, this implementation does use a metaclass to define the<br>enumeration's syntax, and allows for extended base-enumerations so that the<br>

common values in derived classes are identical (a singleton model).  While PEP<br>354 dismisses this approach for its complexity, in practice any perceived<br>complexity, though minimal, is hidden from users of the enumeration.<br>

<br>Unlike PEP 354, enumeration values should only be tested by identity<br>comparison.  This is to emphasize the fact that enumeration values are<br>singletons, much like ``None``.<br><br><br>Acknowledgments<br>===============<br>

<br>This PEP describes the ``flufl.enum`` package by Barry Warsaw.  ``flufl.enum``<br>is based on an example by Jeremy Hylton.  It has been modified and extended<br>by Barry Warsaw for use in the GNU Mailman [5]_ project.  Ben Finney is the<br>

author of the earlier enumeration PEP 354.<br><br><br>References<br>==========<br><br>.. [1] <a href="http://pythonhosted.org/flufl.enum/docs/using.html">http://pythonhosted.org/flufl.enum/docs/using.html</a><br>.. [2] <a href="http://www.python.org/dev/peps/pep-0354/">http://www.python.org/dev/peps/pep-0354/</a><br>

.. [3] <a href="http://mail.python.org/pipermail/python-ideas/2013-January/019003.html">http://mail.python.org/pipermail/python-ideas/2013-January/019003.html</a><br>.. [4] <a href="http://mail.python.org/pipermail/python-ideas/2013-February/019373.html">http://mail.python.org/pipermail/python-ideas/2013-February/019373.html</a><br>

.. [5] <a href="http://www.list.org">http://www.list.org</a><br><br><br>Copyright<br>=========<br><br>This document has been placed in the public domain.<br><br><br>Todo<br>====<br><br> * Mark PEP 354 "superseded by" this one, if accepted<br>

<br>..<br>   Local Variables:<br>   mode: indented-text<br>   indent-tabs-mode: nil<br>   sentence-end-double-space: t<br>   fill-column: 70<br>   coding: utf-8<br>   End:<br><br><div><div><div><div><div><div><div><br></div>

</div></div></div></div></div></div></div>