Overhaul PEP 435 to describe the current state of decisions, which deviates
from flufl.enum: 1. type(Color.red) == Color 2. All attrs of an enum that are not dunders and are not descriptors become enumeration values 3. Given class Color(Enum), it's only valid to subclass Color if Color defines no enumeration values 4. As a corollary to (3), class IntEnum(int, Enum) is also part of the stdlib (and serves as an example for others) Other issues that were contended recently but seem to have gotten resolved: A. __call__ for by-value access, explicit getattr() for by-name access. __getitem__ is removed from Enum B. __int__ is removed from Enum. It's present by definition in IntEnum NOTE: this is still an early draft that is in the process of being reviewed, so please treat it accordingly.
This commit is contained in:
parent
801e07188f
commit
d223705ffc
459
pep-0435.txt
459
pep-0435.txt
|
@ -3,22 +3,20 @@ Title: Adding an Enum type to the Python standard library
|
||||||
Version: $Revision$
|
Version: $Revision$
|
||||||
Last-Modified: $Date$
|
Last-Modified: $Date$
|
||||||
Author: Barry Warsaw <barry@python.org>,
|
Author: Barry Warsaw <barry@python.org>,
|
||||||
Eli Bendersky <eliben@gmail.com>
|
Eli Bendersky <eliben@gmail.com>,
|
||||||
|
Ethan Furman <ethan@stoneleaf.us>
|
||||||
Status: Draft
|
Status: Draft
|
||||||
Type: Standards Track
|
Type: Standards Track
|
||||||
Content-Type: text/x-rst
|
Content-Type: text/x-rst
|
||||||
Created: 2013-02-23
|
Created: 2013-02-23
|
||||||
Python-Version: 3.4
|
Python-Version: 3.4
|
||||||
Post-History: 2013-02-23
|
Post-History: 2013-02-23, 2013-05-02
|
||||||
|
|
||||||
|
|
||||||
Abstract
|
Abstract
|
||||||
========
|
========
|
||||||
|
|
||||||
This PEP proposes adding an enumeration type to the Python standard library.
|
This PEP proposes adding an enumeration type to the Python standard library.
|
||||||
Specifically, it proposes moving the existing ``flufl.enum`` package by Barry
|
|
||||||
Warsaw into the standard library. Much of this PEP is based on the "using"
|
|
||||||
[1]_ document from the documentation of ``flufl.enum``.
|
|
||||||
|
|
||||||
An enumeration is a set of symbolic names bound to unique, constant values.
|
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
|
Within an enumeration, the values can be compared by identity, and the
|
||||||
|
@ -28,7 +26,7 @@ enumeration itself can be iterated over.
|
||||||
Decision
|
Decision
|
||||||
========
|
========
|
||||||
|
|
||||||
TODO: update decision here once pronouncement is made.
|
TODO: update decision here once pronouncement is made. [1]_
|
||||||
|
|
||||||
|
|
||||||
Status of discussions
|
Status of discussions
|
||||||
|
@ -55,9 +53,11 @@ interoperatiliby with integers is desired. For instance, this is the case for
|
||||||
replacing existing standard library constants (such as ``socket.AF_INET``)
|
replacing existing standard library constants (such as ``socket.AF_INET``)
|
||||||
with enumerations.
|
with enumerations.
|
||||||
|
|
||||||
This PEP is an attempt to formalize this decision as well as discuss a number
|
Further discussion in late April 2013 led to the conclusion that enumeration
|
||||||
of variations that were proposed and can be considered for inclusion.
|
members should belong to the type of their enum: ``type(Color.red) == Color``.
|
||||||
|
Guido has pronounced a decision on this issue [5]_, as well as related issues
|
||||||
|
of not allowing to subclass enums [6]_, unless they define no enumeration
|
||||||
|
members [7]_.
|
||||||
|
|
||||||
Motivation
|
Motivation
|
||||||
==========
|
==========
|
||||||
|
@ -97,229 +97,151 @@ Creating an Enum
|
||||||
|
|
||||||
Enumerations are created using the class syntax, which makes them easy to read
|
Enumerations are created using the class syntax, which makes them easy to read
|
||||||
and write. An alternative creation method is described in `Convenience API`_.
|
and write. An alternative creation method is described in `Convenience API`_.
|
||||||
To define an enumeration, derive from the ``Enum`` class and add attributes
|
To define an enumeration, subclass ``Enum`` as follows::
|
||||||
with assignment to their integer values::
|
|
||||||
|
|
||||||
>>> from enum import Enum
|
>>> from enum import Enum
|
||||||
>>> class Colors(Enum):
|
>>> class Color(Enum):
|
||||||
... red = 1
|
... red = 1
|
||||||
... green = 2
|
... green = 2
|
||||||
... blue = 3
|
... blue = 3
|
||||||
|
|
||||||
Enumeration values have nice, human readable string representations::
|
**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.)
|
||||||
|
|
||||||
>>> print(Colors.red)
|
Enumeration members have human readable string representations::
|
||||||
Colors.red
|
|
||||||
|
|
||||||
...while their repr has more information::
|
>>> print(Color.red)
|
||||||
|
Color.red
|
||||||
|
|
||||||
>>> print(repr(Colors.red))
|
...while their ``repr`` has more information::
|
||||||
<EnumValue: Colors.red [value=1]>
|
|
||||||
|
|
||||||
You can get the enumeration class object from an enumeration value::
|
>>> print(repr(Color.red))
|
||||||
|
Color.red [value=1]
|
||||||
|
|
||||||
>>> cls = Colors.red.enum
|
The *type* of an enumeration member is the enumeration it belongs to::
|
||||||
>>> print(cls.__name__)
|
|
||||||
Colors
|
>>> type(Color.red)
|
||||||
|
<Enum 'Color'>
|
||||||
|
>>> isinstance(Color.green, Color)
|
||||||
|
True
|
||||||
|
>>>
|
||||||
|
|
||||||
Enums also have a property that contains just their item name::
|
Enums also have a property that contains just their item name::
|
||||||
|
|
||||||
>>> print(Colors.red.name)
|
>>> print(Color.red.name)
|
||||||
red
|
red
|
||||||
>>> print(Colors.green.name)
|
|
||||||
green
|
|
||||||
>>> print(Colors.blue.name)
|
|
||||||
blue
|
|
||||||
|
|
||||||
The str and repr of the enumeration class also provides useful information::
|
Enumerations support iteration, in definition order::
|
||||||
|
|
||||||
>>> print(Colors)
|
>>> class Shake(Enum):
|
||||||
<Colors {red: 1, green: 2, blue: 3}>
|
... vanilla = 7
|
||||||
>>> print(repr(Colors))
|
... chocolate = 4
|
||||||
<Colors {red: 1, green: 2, blue: 3}>
|
... cookies = 9
|
||||||
|
... mint = 3
|
||||||
|
...
|
||||||
|
>>> for shake in Shake:
|
||||||
|
... print(shake)
|
||||||
|
...
|
||||||
|
Shake.vanilla
|
||||||
|
Shake.chocolate
|
||||||
|
Shake.cookies
|
||||||
|
Shake.mint
|
||||||
|
|
||||||
The ``Enum`` class supports iteration. Iteration order is undefined::
|
Enumeration members are hashable, so they can be used in dictionaries and sets::
|
||||||
|
|
||||||
>>> from operator import attrgetter
|
|
||||||
>>> by_value = attrgetter('value')
|
|
||||||
>>> class FiveColors(Enum):
|
|
||||||
... pink = 4
|
|
||||||
... cyan = 5
|
|
||||||
... green = 2
|
|
||||||
... blue = 3
|
|
||||||
... red = 1
|
|
||||||
>>> [v.name for v in sorted(FiveColors, by_value)]
|
|
||||||
['red', 'green', 'blue', 'pink', 'cyan']
|
|
||||||
|
|
||||||
Iteration order over `IntEnum`_ enumerations are guaranteed to be
|
|
||||||
sorted by value.
|
|
||||||
|
|
||||||
>>> class Toppings(IntEnum):
|
|
||||||
... anchovies = 4
|
|
||||||
... black_olives = 8
|
|
||||||
... cheese = 2
|
|
||||||
... dried_tomatoes = 16
|
|
||||||
... eggplant = 1
|
|
||||||
|
|
||||||
>>> for value in Toppings:
|
|
||||||
... print(value.name, '=', value.value)
|
|
||||||
eggplant = 1
|
|
||||||
cheese = 2
|
|
||||||
anchovies = 4
|
|
||||||
black_olives = 8
|
|
||||||
dried_tomatoes = 16
|
|
||||||
|
|
||||||
Enumeration values are hashable, so they can be used in dictionaries and sets::
|
|
||||||
|
|
||||||
>>> apples = {}
|
>>> apples = {}
|
||||||
>>> apples[Colors.red] = 'red delicious'
|
>>> apples[Color.red] = 'red delicious'
|
||||||
>>> apples[Colors.green] = 'granny smith'
|
>>> apples[Color.green] = 'granny smith'
|
||||||
>>> apples
|
>>> apples
|
||||||
{<EnumValue: Colors.green [value=2]>: 'granny smith', <EnumValue: Colors.red [value=1]>: 'red delicious'}
|
{Color.red [value=1]: 'red delicious', Color.green [value=2]: 'granny smith'}
|
||||||
|
|
||||||
Programmatic access to enum values
|
Programmatic access to enumeration members
|
||||||
----------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
Sometimes it's useful to access values in enumerations programmatically (i.e.
|
Sometimes it's useful to access members in enumerations programmatically (i.e.
|
||||||
situations where ``Colors.red`` won't do because the exact color is not known
|
situations where ``Color.red`` won't do because the exact color is not known
|
||||||
at program-writing time). ``Enum`` allows such access by value::
|
at program-writing time). ``Enum`` allows such access::
|
||||||
|
|
||||||
>>> Colors[1]
|
>>> Color(1)
|
||||||
<EnumValue: Colors.red [value=1]>
|
Color.red [value=1]
|
||||||
>>> Colors[2]
|
>>> Color(3)
|
||||||
<EnumValue: Colors.green [value=2]>
|
Color.blue [value=3]
|
||||||
|
|
||||||
For consistency, an ``EnumValue`` can be used in the same access pattern::
|
If you want to access enum members by *name*, ``Enum`` works as expected with
|
||||||
|
|
||||||
>>> Colors[Colors.red]
|
|
||||||
<EnumValue: Colors.red [value=1]>
|
|
||||||
>>> Colors[Colors.green]
|
|
||||||
<EnumValue: Colors.green [value=2]>
|
|
||||||
|
|
||||||
If you want to access enum values by *name*, ``Enum`` works as expected with
|
|
||||||
``getattr``::
|
``getattr``::
|
||||||
|
|
||||||
>>> getattr(Colors, 'red')
|
>>> getattr(Color, 'red')
|
||||||
<EnumValue: Colors.red [value=1]>
|
Color.red [value=1]
|
||||||
>>> getattr(Colors, 'green')
|
>>> getattr(Color, 'green')
|
||||||
<EnumValue: Colors.green [value=2]>
|
Color.green [value=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. By-value lookup
|
||||||
|
will then access the *earliest defined* member::
|
||||||
|
|
||||||
|
>>> class Shape(Enum):
|
||||||
|
... square = 2
|
||||||
|
... diamond = 1
|
||||||
|
... circle = 3
|
||||||
|
... alias_for_square = 2
|
||||||
|
...
|
||||||
|
>>> Shape.square
|
||||||
|
Shape.square [value=2]
|
||||||
|
>>> Shape.alias_for_square
|
||||||
|
Shape.square [value=2]
|
||||||
|
>>> Shape(2)
|
||||||
|
Shape.square [value=2]
|
||||||
|
|
||||||
Comparisons
|
Comparisons
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Enumeration values are compared by identity::
|
Enumeration members are compared by identity::
|
||||||
|
|
||||||
>>> Colors.red is Colors.red
|
>>> Color.red is Color.red
|
||||||
True
|
True
|
||||||
>>> Colors.blue is Colors.blue
|
>>> Color.red is Color.blue
|
||||||
True
|
|
||||||
>>> Colors.red is not Colors.blue
|
|
||||||
True
|
|
||||||
>>> Colors.blue is Colors.red
|
|
||||||
False
|
False
|
||||||
|
>>> Color.red is not Color.blue
|
||||||
|
True
|
||||||
|
|
||||||
Ordered comparisons between enumeration values are *not* supported. Enums are
|
Ordered comparisons between enumeration values are *not* supported. Enums are
|
||||||
not integers (but see `IntEnum`_ below)::
|
not integers (but see `IntEnum`_ below)::
|
||||||
|
|
||||||
>>> Colors.red < Colors.blue
|
>>> Color.red < Color.blue
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
File "<stdin>", line 1, in <module>
|
||||||
NotImplementedError
|
TypeError: unorderable types: Color() < Color()
|
||||||
>>> Colors.red <= Colors.blue
|
|
||||||
Traceback (most recent call last):
|
|
||||||
...
|
|
||||||
NotImplementedError
|
|
||||||
>>> Colors.blue > Colors.green
|
|
||||||
Traceback (most recent call last):
|
|
||||||
...
|
|
||||||
NotImplementedError
|
|
||||||
>>> Colors.blue >= Colors.green
|
|
||||||
Traceback (most recent call last):
|
|
||||||
...
|
|
||||||
NotImplementedError
|
|
||||||
|
|
||||||
Equality comparisons are defined though::
|
Equality comparisons are defined though::
|
||||||
|
|
||||||
>>> Colors.blue == Colors.blue
|
>>> Color.blue == Color.red
|
||||||
True
|
False
|
||||||
>>> Colors.green != Colors.blue
|
>>> Color.blue == Color.blue
|
||||||
True
|
True
|
||||||
|
|
||||||
Comparisons against non-enumeration values will always compare not equal::
|
Comparisons against non-enumeration values will always compare not equal
|
||||||
|
(again, ``IntEnum`` was explicitly designed to behave differently, see
|
||||||
|
below)::
|
||||||
|
|
||||||
>>> Colors.green == 2
|
>>> Color.blue == 2
|
||||||
False
|
|
||||||
>>> Colors.blue == 3
|
|
||||||
False
|
|
||||||
>>> Colors.green != 3
|
|
||||||
True
|
|
||||||
>>> Colors.green == 'green'
|
|
||||||
False
|
False
|
||||||
|
|
||||||
|
Allowed members and attributs of enumerations
|
||||||
Extending enumerations by subclassing
|
---------------------------------------------
|
||||||
-------------------------------------
|
|
||||||
|
|
||||||
You can extend previously defined Enums by subclassing::
|
|
||||||
|
|
||||||
>>> class MoreColors(Colors):
|
|
||||||
... pink = 4
|
|
||||||
... cyan = 5
|
|
||||||
|
|
||||||
When extended in this way, the base enumeration's values are identical to the
|
|
||||||
same named values in the derived class::
|
|
||||||
|
|
||||||
>>> Colors.red is MoreColors.red
|
|
||||||
True
|
|
||||||
>>> Colors.blue is MoreColors.blue
|
|
||||||
True
|
|
||||||
|
|
||||||
However, these are not doing comparisons against the integer
|
|
||||||
equivalent values, because if you define an enumeration with similar
|
|
||||||
item names and integer values, they will not be identical::
|
|
||||||
|
|
||||||
>>> class OtherColors(Enum):
|
|
||||||
... red = 1
|
|
||||||
... blue = 2
|
|
||||||
... yellow = 3
|
|
||||||
>>> Colors.red is OtherColors.red
|
|
||||||
False
|
|
||||||
>>> Colors.blue is not OtherColors.blue
|
|
||||||
True
|
|
||||||
|
|
||||||
Because ``Colors`` and ``OtherColors`` are unrelated enumerations,
|
|
||||||
their values are not equal, and thus they may exist in the same set,
|
|
||||||
or as distinct keys in the same dictionary::
|
|
||||||
|
|
||||||
>>> Colors.red == OtherColors.red
|
|
||||||
False
|
|
||||||
>>> len(set((Colors.red, OtherColors.red)))
|
|
||||||
2
|
|
||||||
|
|
||||||
You may not define two enumeration values with the same integer value::
|
|
||||||
|
|
||||||
>>> class Bad(Enum):
|
|
||||||
... cartman = 1
|
|
||||||
... stan = 2
|
|
||||||
... kyle = 3
|
|
||||||
... kenny = 3 # Oops!
|
|
||||||
... butters = 4
|
|
||||||
Traceback (most recent call last):
|
|
||||||
...
|
|
||||||
ValueError: Conflicting enums with value '3': 'kenny' and 'kyle'
|
|
||||||
|
|
||||||
You also may not duplicate values in derived enumerations::
|
|
||||||
|
|
||||||
>>> class BadColors(Colors):
|
|
||||||
... yellow = 4
|
|
||||||
... chartreuse = 2 # Oops!
|
|
||||||
Traceback (most recent call last):
|
|
||||||
...
|
|
||||||
ValueError: Conflicting enums with value '2': 'green' and 'chartreuse'
|
|
||||||
|
|
||||||
|
|
||||||
Enumeration values
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The examples above use integers for enumeration values. Using integers is
|
The examples above use integers for enumeration values. Using integers is
|
||||||
short and handy (and provided by default by the `Convenience API`_), but not
|
short and handy (and provided by default by the `Convenience API`_), but not
|
||||||
|
@ -330,9 +252,9 @@ enumerations can have arbitrary values. The following example uses strings::
|
||||||
>>> class SpecialId(Enum):
|
>>> class SpecialId(Enum):
|
||||||
... selector = '$IM($N)'
|
... selector = '$IM($N)'
|
||||||
... adaptor = '~$IM'
|
... adaptor = '~$IM'
|
||||||
...
|
...
|
||||||
>>> SpecialId.selector
|
>>> SpecialId.selector
|
||||||
<EnumValue: SpecialId.selector [value=$IM($N)]>
|
SpecialId.selector [value='$IM($N)']
|
||||||
>>> SpecialId.selector.value
|
>>> SpecialId.selector.value
|
||||||
'$IM($N)'
|
'$IM($N)'
|
||||||
>>> a = SpecialId.adaptor
|
>>> a = SpecialId.adaptor
|
||||||
|
@ -342,8 +264,6 @@ enumerations can have arbitrary values. The following example uses strings::
|
||||||
True
|
True
|
||||||
>>> print(a)
|
>>> print(a)
|
||||||
SpecialId.adaptor
|
SpecialId.adaptor
|
||||||
>>> print(a.value)
|
|
||||||
~$IM
|
|
||||||
|
|
||||||
Here ``Enum`` is used to provide readable (and syntactically valid!) names for
|
Here ``Enum`` is used to provide readable (and syntactically valid!) names for
|
||||||
some special values, as well as group them together.
|
some special values, as well as group them together.
|
||||||
|
@ -353,14 +273,73 @@ very special cases. Code will be most readable when actual values of
|
||||||
enumerations aren't important and enumerations are just used for their
|
enumerations aren't important and enumerations are just used for their
|
||||||
naming and comparison properties.
|
naming and comparison properties.
|
||||||
|
|
||||||
|
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 [value=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; 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 subclass 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 [6]_. 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
|
IntEnum
|
||||||
-------
|
-------
|
||||||
|
|
||||||
A variation of ``Enum`` is proposed where the enumeration values also
|
A variation of ``Enum`` is proposed which is also a subclass of ``int``.
|
||||||
subclasses ``int`` - ``IntEnum``. These values can be compared to
|
Members of an ``IntEnum`` can be compared to integers; by extension,
|
||||||
integers; by extension, enumerations of different types can also be
|
integer enumerations of different types can also be compared to each other::
|
||||||
compared to each other::
|
|
||||||
|
|
||||||
>>> from enum import IntEnum
|
>>> from enum import IntEnum
|
||||||
>>> class Shape(IntEnum):
|
>>> class Shape(IntEnum):
|
||||||
|
@ -384,11 +363,11 @@ However they still can't be compared to ``Enum``::
|
||||||
... circle = 1
|
... circle = 1
|
||||||
... square = 2
|
... square = 2
|
||||||
...
|
...
|
||||||
>>> class Colors(Enum):
|
>>> class Color(Enum):
|
||||||
... red = 1
|
... red = 1
|
||||||
... green = 2
|
... green = 2
|
||||||
...
|
...
|
||||||
>>> Shape.circle == Colors.red
|
>>> Shape.circle == Color.red
|
||||||
False
|
False
|
||||||
|
|
||||||
``IntEnum`` values behave like integers in other ways you'd expect::
|
``IntEnum`` values behave like integers in other ways you'd expect::
|
||||||
|
@ -425,21 +404,23 @@ Convenience API
|
||||||
|
|
||||||
The ``Enum`` class is callable, providing the following convenience API::
|
The ``Enum`` class is callable, providing the following convenience API::
|
||||||
|
|
||||||
>>> Animals = Enum('Animals', 'ant bee cat dog')
|
>>> Animal = Enum('Animal', 'ant bee cat dog')
|
||||||
>>> Animals
|
>>> Animal
|
||||||
<Animals {ant: 1, bee: 2, cat: 3, dog: 4}>
|
<Enum 'Animal'>
|
||||||
>>> Animals.ant
|
>>> Animal.ant
|
||||||
<EnumValue: Animals.ant [value=1]>
|
Animal.ant [value=1]
|
||||||
>>> Animals.ant.value
|
>>> Animal.ant.value
|
||||||
1
|
1
|
||||||
|
>>> list(Animal)
|
||||||
|
[Animal.ant [value=1], Animal.bee [value=2], Animal.cat [value=3], Animal.dog [value=4]]
|
||||||
|
|
||||||
The semantics of this API resemble ``namedtuple``. The first argument of
|
The semantics of this API resemble ``namedtuple``. The first argument of
|
||||||
the call to ``Enum`` is the name of the enumeration. The second argument is
|
the call to ``Enum`` is the name of the enumeration. The second argument is
|
||||||
a source of enumeration value names. It can be a whitespace-separated string
|
a source of enumeration member names. It can be a whitespace-separated string
|
||||||
of names, a sequence of names or a sequence of 2-tuples with key/value pairs.
|
of names, a sequence of names or a sequence of 2-tuples with key/value pairs.
|
||||||
The last option enables assigning arbitrary values to enumerations; the others
|
The last option enables assigning arbitrary values to enumerations; the others
|
||||||
auto-assign increasing integers starting with 1. A new class derived from
|
auto-assign increasing integers starting with 1. A new class derived from
|
||||||
``Enum`` is returned. In other words, the above assignment to ``Animals`` is
|
``Enum`` is returned. In other words, the above assignment to ``Animal`` is
|
||||||
equivalent to::
|
equivalent to::
|
||||||
|
|
||||||
>>> class Animals(Enum):
|
>>> class Animals(Enum):
|
||||||
|
@ -448,26 +429,22 @@ equivalent to::
|
||||||
... cat = 3
|
... cat = 3
|
||||||
... dog = 4
|
... dog = 4
|
||||||
|
|
||||||
Examples of alternative name/value specifications::
|
|
||||||
|
|
||||||
>>> Enum('Animals', ['ant', 'bee', 'cat', 'dog'])
|
|
||||||
<Animals {ant: 1, bee: 2, cat: 3, dog: 4}>
|
|
||||||
>>> Enum('Animals', (('ant', 'one'), ('bee', 'two'), ('cat', 'three'), ('dog', 'four')))
|
|
||||||
<Animals {dog: four, ant: one, cat: three, bee: two}>
|
|
||||||
|
|
||||||
The second argument can also be a dictionary mapping names to values::
|
|
||||||
|
|
||||||
>>> levels = dict(debug=10, info=20, warning=30, severe=40)
|
|
||||||
>>> Enum('Levels', levels)
|
|
||||||
<Levels {debug: 10, info: 20, warning: 30, severe: 40}>
|
|
||||||
|
|
||||||
|
|
||||||
Proposed variations
|
Proposed variations
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Some variations were proposed during the discussions in the mailing list.
|
Some variations were proposed during the discussions in the mailing list.
|
||||||
Here's some of the more popular ones.
|
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
|
Not having to specify values for enums
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
@ -487,7 +464,6 @@ Cons: involves much magic in the implementation, which makes even the
|
||||||
definition of such enums baffling when first seen. Besides, explicit is
|
definition of such enums baffling when first seen. Besides, explicit is
|
||||||
better than implicit.
|
better than implicit.
|
||||||
|
|
||||||
|
|
||||||
Using special names or forms to auto-assign enum values
|
Using special names or forms to auto-assign enum values
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
|
|
||||||
|
@ -519,7 +495,6 @@ extend it, especially for large enumerations.
|
||||||
Cons: actually longer to type in many simple cases. The argument of explicit
|
Cons: actually longer to type in many simple cases. The argument of explicit
|
||||||
vs. implicit applies here as well.
|
vs. implicit applies here as well.
|
||||||
|
|
||||||
|
|
||||||
Use-cases in the standard library
|
Use-cases in the standard library
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
|
@ -553,51 +528,24 @@ cases for replacing internal state constants with enums. The same can be said
|
||||||
about a lot of networking code (especially implementation of protocols) and
|
about a lot of networking code (especially implementation of protocols) and
|
||||||
can be seen in test protocols written with the Tulip library as well.
|
can be seen in test protocols written with the Tulip library as well.
|
||||||
|
|
||||||
|
|
||||||
Differences from PEP 354
|
|
||||||
========================
|
|
||||||
|
|
||||||
Unlike PEP 354, enumeration values are not defined as a sequence of strings,
|
|
||||||
but as attributes of a class. This design was chosen because it was felt that
|
|
||||||
class syntax is more readable.
|
|
||||||
|
|
||||||
Unlike PEP 354, enumeration values require an explicit integer value. This
|
|
||||||
difference recognizes that enumerations often represent real-world values, or
|
|
||||||
must interoperate with external real-world systems. For example, to store an
|
|
||||||
enumeration in a database, it is better to convert it to an integer on the way
|
|
||||||
in and back to an enumeration on the way out. Providing an integer value also
|
|
||||||
provides an explicit ordering. However, there is no automatic conversion to
|
|
||||||
and from the integer values, because explicit is better than implicit.
|
|
||||||
|
|
||||||
Unlike PEP 354, this implementation does use a metaclass to define the
|
|
||||||
enumeration's syntax, and allows for extended base-enumerations so that the
|
|
||||||
common values in derived classes are identical (a singleton model). While PEP
|
|
||||||
354 dismisses this approach for its complexity, in practice any perceived
|
|
||||||
complexity, though minimal, is hidden from users of the enumeration.
|
|
||||||
|
|
||||||
Unlike PEP 354, enumeration values should only be tested by identity
|
|
||||||
comparison. This is to emphasize the fact that enumeration values are
|
|
||||||
singletons, much like ``None``.
|
|
||||||
|
|
||||||
|
|
||||||
Acknowledgments
|
Acknowledgments
|
||||||
===============
|
===============
|
||||||
|
|
||||||
This PEP describes the ``flufl.enum`` package by Barry Warsaw. ``flufl.enum``
|
This PEP was initially proposing including the ``flufl.enum`` package [8]_
|
||||||
is based on an example by Jeremy Hylton. It has been modified and extended
|
by Barry Warsaw into the stdlib, and is inspired in large parts by it.
|
||||||
by Barry Warsaw for use in the GNU Mailman [5]_ project. Ben Finney is the
|
Ben Finney is the author of the earlier enumeration PEP 354.
|
||||||
author of the earlier enumeration PEP 354.
|
|
||||||
|
|
||||||
|
|
||||||
References
|
References
|
||||||
==========
|
==========
|
||||||
|
|
||||||
.. [1] http://pythonhosted.org/flufl.enum/docs/using.html
|
.. [1] Placeholder for pronouncement
|
||||||
.. [2] http://www.python.org/dev/peps/pep-0354/
|
.. [2] http://www.python.org/dev/peps/pep-0354/
|
||||||
.. [3] http://mail.python.org/pipermail/python-ideas/2013-January/019003.html
|
.. [3] http://mail.python.org/pipermail/python-ideas/2013-January/019003.html
|
||||||
.. [4] http://mail.python.org/pipermail/python-ideas/2013-February/019373.html
|
.. [4] http://mail.python.org/pipermail/python-ideas/2013-February/019373.html
|
||||||
.. [5] http://www.list.org
|
.. [5] http://mail.python.org/pipermail/python-dev/2013-April/125687.html
|
||||||
|
.. [6] http://mail.python.org/pipermail/python-dev/2013-April/125716.html
|
||||||
|
.. [7] http://mail.python.org/pipermail/python-dev/2013-May/125859.html
|
||||||
|
.. [8] http://pythonhosted.org/flufl.enum/
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
=========
|
=========
|
||||||
|
@ -608,7 +556,8 @@ This document has been placed in the public domain.
|
||||||
Todo
|
Todo
|
||||||
====
|
====
|
||||||
|
|
||||||
* Mark PEP 354 "superseded by" this one, if accepted
|
* Mark PEP 354 "superseded by" this one, if accepted
|
||||||
|
* The last revision where flufl.enum was the approach is cb3c18a080a3
|
||||||
|
|
||||||
..
|
..
|
||||||
Local Variables:
|
Local Variables:
|
||||||
|
|
Loading…
Reference in New Issue