PEP:            354
Title:          Enumerations in Python
Version:        $Revision$
Last-Modified:  $Date$
Author:         Ben Finney <ben+python@benfinney.id.au>
Status:         Superseded
Type:           Standards Track
Content-Type:   text/x-rst
Created:        20-Dec-2005
Python-Version: 2.6
Post-History:   20-Dec-2005
Superseded-By:  435


Rejection Notice
================

This PEP has been rejected.  This doesn't slot nicely into any of the
existing modules (like collections), and the Python standard library
eschews having lots of individual data structures in their own
modules.  Also, the PEP has generated no widespread interest.  For
those who need enumerations, there are cookbook recipes and PyPI
packages that meet these needs.

*Note: this PEP was superseded by PEP 435, which has been accepted in
May 2013.*

Abstract
========

This PEP specifies an enumeration data type for Python.

An enumeration is an exclusive set of symbolic names bound to
arbitrary unique values.  Values within an enumeration can be iterated
and compared, but the values have no inherent relationship to values
outside the enumeration.


Motivation
==========

The properties of an enumeration are useful for defining an immutable,
related set of constant values that have a defined sequence but no
inherent 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, and that operations without meaning
("Wednesday times two") are not defined for these values.


Specification
=============

An enumerated type is created from a sequence of arguments to the
type's constructor::

    >>> Weekdays = enum('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat')
    >>> Grades = enum('A', 'B', 'C', 'D', 'F')

Enumerations with no values are meaningless.  The exception
``EnumEmptyError`` is raised if the constructor is called with no
value arguments.

The values are bound to attributes of the new enumeration object::

    >>> today = Weekdays.mon

The values can be compared::

    >>> if today == Weekdays.fri:
    ...     print "Get ready for the weekend"

Values within an enumeration cannot be meaningfully compared except
with values from the same enumeration.  The comparison operation
functions return ``NotImplemented`` [#CMP-NOTIMPLEMENTED]_ when a
value from an enumeration is compared against any value not from the
same enumeration or of a different type::

    >>> gym_night = Weekdays.wed
    >>> gym_night.__cmp__(Weekdays.mon)
    1
    >>> gym_night.__cmp__(Weekdays.wed)
    0
    >>> gym_night.__cmp__(Weekdays.fri)
    -1
    >>> gym_night.__cmp__(23)
    NotImplemented
    >>> gym_night.__cmp__("wed")
    NotImplemented
    >>> gym_night.__cmp__(Grades.B)
    NotImplemented

This allows the operation to succeed, evaluating to a boolean value::

    >>> gym_night = Weekdays.wed
    >>> gym_night < Weekdays.mon
    False
    >>> gym_night < Weekdays.wed
    False
    >>> gym_night < Weekdays.fri
    True
    >>> gym_night < 23
    False
    >>> gym_night > 23
    True
    >>> gym_night > "wed"
    True
    >>> gym_night > Grades.B
    True

Coercing a value from an enumeration to a ``str`` results in the
string that was specified for that value when constructing the
enumeration::

    >>> gym_night = Weekdays.wed
    >>> str(gym_night)
    'wed'

The sequence index of each value from an enumeration is exported as an
integer via that value's ``index`` attribute::

    >>> gym_night = Weekdays.wed
    >>> gym_night.index
    3

An enumeration can be iterated, returning its values in the sequence
they were specified when the enumeration was created::

    >>> print [str(day) for day in Weekdays]
    ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']

Values from an enumeration are hashable, and can be used as dict
keys::

    >>> plans = {}
    >>> plans[Weekdays.sat] = "Feed the horse"

The normal usage of enumerations is to provide a set of possible
values for a data type, which can then be used to map to other
information about the values::

    >>> for report_grade in Grades:
    ...     report_students[report_grade] = \
    ...         [s for s in students if students.grade == report_grade]


Rationale -- Other designs considered
=====================================

All in one class
----------------

Some implementations have the enumeration and its values all as
attributes of a single object or class.

This PEP specifies a design where the enumeration is a container, and
the values are simple comparables.  It was felt that attempting to
place all the properties of enumeration within a single class
complicates the design without apparent benefit.


Metaclass for creating enumeration classes
------------------------------------------

The enumerations specified in this PEP are instances of an ``enum``
type.  Some alternative designs implement each enumeration as its own
class, and a metaclass to define common properties of all
enumerations.

One motivation for having a class (rather than an instance) for each
enumeration is to allow subclasses of enumerations, extending and
altering an existing enumeration.  A class, though, implies that
instances of that class will be created; it is difficult to imagine
what it means to have separate instances of a "days of the week"
class, where each instance contains all days.  This usually leads to
having each class follow the Singleton pattern, further complicating
the design.

In contrast, this PEP specifies enumerations that are not expected to
be extended or modified.  It is, of course, possible to create a new
enumeration from the string values of an existing one, or even
subclass the ``enum`` type if desired.


Values related to other types
-----------------------------

Some designs express a strong relationship to some other value, such
as a particular integer or string, for each enumerated value.

This results in using such values in contexts where the enumeration
has no meaning, and unnecessarily complicates the design.  The
enumerated values specified in this PEP export the values used to
create them, and can be compared for equality with any other value,
but sequence comparison with values outside the enumeration is
explicitly not implemented.


Hiding attributes of enumerated values
--------------------------------------

A previous design had the enumerated values hiding as much as possible
about their implementation, to the point of not exporting the string
key and sequence index.

The design in this PEP acknowledges that programs will often find it
convenient to know the enumerated value's enumeration type, sequence
index, and string key specified for the value.  These are exported by
the enumerated value as attributes.


Implementation
==============

This design is based partly on a recipe [#ENUM-RECIPE]_ from the
Python Cookbook.

The PyPI package ``enum`` [#ENUM-PACKAGE]_ provides a Python
implementation of the data types described in this PEP.


References and Footnotes
========================

..  [#CMP-NOTIMPLEMENTED]
    The ``NotImplemented`` return value from comparison operations
    signals the Python interpreter to attempt alternative comparisons
    or other fallbacks.
    <http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy>

..  [#ENUM-RECIPE]
    "First Class Enums in Python", Zoran Isailovski,
    Python Cookbook recipe 413486
    <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413486>

..  [#ENUM-PACKAGE]
    Python Package Index, package ``enum``
    <http://cheeseshop.python.org/pypi/enum/>


Copyright
=========

This document has been placed in the public domain.


..
   Local Variables:
   mode: indented-text
   indent-tabs-mode: nil
   sentence-end-double-space: t
   fill-column: 70
   End:
