Python Enhancement Proposals

PEP 354 – Enumerations in Python

PEP
354
Title
Enumerations in Python
Author
Ben Finney <ben+python at benfinney.id.au>
Status
Superseded
Type
Standards Track
Created
20-Dec-2005
Python-Version
2.6
Post-History
20-Dec-2005
Superseded-By
435

Contents

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 1 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.

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 2 from the Python Cookbook.

The PyPI package enum 3 provides a Python implementation of the data types described in this PEP.

References and Footnotes

1
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>
2
“First Class Enums in Python”, Zoran Isailovski, Python Cookbook recipe 413486 <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413486>
3
Python Package Index, package enum <http://cheeseshop.python.org/pypi/enum/>

Source: https://github.com/python/peps/blob/master/pep-0354.txt

Last modified: 2017-11-11 19:28:55 GMT