Update PEP 435 with latest decisions and add some clarifications:
1. getitem syntax come-back 2. Define iteration in presence of aliases 3. __aliases__ 4. s/Convenience/Functional/ API 5. Don't say enums created with the functional API can't be pickled, but provide the usual precaution from the doc of pickle
This commit is contained in:
parent
fa6769dc34
commit
440ad4c854
156
pep-0435.txt
156
pep-0435.txt
|
@ -96,7 +96,7 @@ 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 `Functional API`_.
|
||||||
To define an enumeration, subclass ``Enum`` as follows::
|
To define an enumeration, subclass ``Enum`` as follows::
|
||||||
|
|
||||||
>>> from enum import Enum
|
>>> from enum import Enum
|
||||||
|
@ -118,7 +118,7 @@ Enumeration members have human readable string representations::
|
||||||
...while their ``repr`` has more information::
|
...while their ``repr`` has more information::
|
||||||
|
|
||||||
>>> print(repr(Color.red))
|
>>> print(repr(Color.red))
|
||||||
Color.red [value=1]
|
<Color.red: 1>
|
||||||
|
|
||||||
The *type* of an enumeration member is the enumeration it belongs to::
|
The *type* of an enumeration member is the enumeration it belongs to::
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ The *type* of an enumeration member is the enumeration it belongs to::
|
||||||
<Enum 'Color'>
|
<Enum 'Color'>
|
||||||
>>> isinstance(Color.green, Color)
|
>>> isinstance(Color.green, Color)
|
||||||
True
|
True
|
||||||
>>>
|
>>>
|
||||||
|
|
||||||
Enums also have a property that contains just their item name::
|
Enums also have a property that contains just their item name::
|
||||||
|
|
||||||
|
@ -140,10 +140,10 @@ Enumerations support iteration, in definition order::
|
||||||
... chocolate = 4
|
... chocolate = 4
|
||||||
... cookies = 9
|
... cookies = 9
|
||||||
... mint = 3
|
... mint = 3
|
||||||
...
|
...
|
||||||
>>> for shake in Shake:
|
>>> for shake in Shake:
|
||||||
... print(shake)
|
... print(shake)
|
||||||
...
|
...
|
||||||
Shake.vanilla
|
Shake.vanilla
|
||||||
Shake.chocolate
|
Shake.chocolate
|
||||||
Shake.cookies
|
Shake.cookies
|
||||||
|
@ -155,7 +155,8 @@ Enumeration members are hashable, so they can be used in dictionaries and sets::
|
||||||
>>> apples[Color.red] = 'red delicious'
|
>>> apples[Color.red] = 'red delicious'
|
||||||
>>> apples[Color.green] = 'granny smith'
|
>>> apples[Color.green] = 'granny smith'
|
||||||
>>> apples
|
>>> apples
|
||||||
{Color.red [value=1]: 'red delicious', Color.green [value=2]: 'granny smith'}
|
{<Color.red: 1>: 'red delicious', <Color.green: 2>: 'granny smith'}
|
||||||
|
|
||||||
|
|
||||||
Programmatic access to enumeration members
|
Programmatic access to enumeration members
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
@ -165,17 +166,17 @@ situations where ``Color.red`` won't do because the exact color is not known
|
||||||
at program-writing time). ``Enum`` allows such access::
|
at program-writing time). ``Enum`` allows such access::
|
||||||
|
|
||||||
>>> Color(1)
|
>>> Color(1)
|
||||||
Color.red [value=1]
|
<Color.red: 1>
|
||||||
>>> Color(3)
|
>>> Color(3)
|
||||||
Color.blue [value=3]
|
<Colro.blue: 3>
|
||||||
|
|
||||||
If you want to access enum members by *name*, ``Enum`` works as expected with
|
If you want to access enum members by *name*, use item access::
|
||||||
``getattr``::
|
|
||||||
|
>>> Color['red']
|
||||||
|
<Color.red: 1>
|
||||||
|
>>> Color['green']
|
||||||
|
<Color.green: 2>
|
||||||
|
|
||||||
>>> getattr(Color, 'red')
|
|
||||||
Color.red [value=1]
|
|
||||||
>>> getattr(Color, 'green')
|
|
||||||
Color.green [value=2]
|
|
||||||
|
|
||||||
Duplicating enum members and values
|
Duplicating enum members and values
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
@ -185,26 +186,39 @@ Having two enum members with the same name is invalid::
|
||||||
>>> class Shape(Enum):
|
>>> class Shape(Enum):
|
||||||
... square = 2
|
... square = 2
|
||||||
... square = 3
|
... square = 3
|
||||||
...
|
...
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Attempted to reuse key: square
|
TypeError: Attempted to reuse key: square
|
||||||
|
|
||||||
However, two enum members are allowed to have the same value. By-value lookup
|
However, two enum members are allowed to have the same value. Given two members
|
||||||
will then access the *earliest defined* member::
|
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.
|
||||||
|
|
||||||
>>> class Shape(Enum):
|
>>> class Shape(Enum):
|
||||||
... square = 2
|
... square = 2
|
||||||
... diamond = 1
|
... diamond = 1
|
||||||
... circle = 3
|
... circle = 3
|
||||||
... alias_for_square = 2
|
... alias_for_square = 2
|
||||||
...
|
...
|
||||||
>>> Shape.square
|
>>> Shape.square
|
||||||
Shape.square [value=2]
|
<Shape.square: 2>
|
||||||
>>> Shape.alias_for_square
|
>>> Shape.alias_for_square
|
||||||
Shape.square [value=2]
|
<Shape.square: 2>
|
||||||
>>> Shape(2)
|
>>> Shape(2)
|
||||||
Shape.square [value=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>]
|
||||||
|
|
||||||
|
If access to aliases is required for some reason, use the special attribute
|
||||||
|
``__aliases__``::
|
||||||
|
|
||||||
|
>>> Shape.__aliases__
|
||||||
|
['alias_for_square']
|
||||||
|
|
||||||
|
|
||||||
Comparisons
|
Comparisons
|
||||||
-----------
|
-----------
|
||||||
|
@ -240,38 +254,15 @@ below)::
|
||||||
>>> Color.blue == 2
|
>>> Color.blue == 2
|
||||||
False
|
False
|
||||||
|
|
||||||
Allowed members and attributs of enumerations
|
|
||||||
---------------------------------------------
|
Allowed members and attributes of enumerations
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
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 `Functional API`_), but not
|
||||||
strictly enforced. In the vast majority of use-cases, one doesn't care what
|
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,
|
the actual value of an enumeration is. But if the value *is* important,
|
||||||
enumerations can have arbitrary values. The following example uses strings::
|
enumerations can have arbitrary values.
|
||||||
|
|
||||||
>>> class SpecialId(Enum):
|
|
||||||
... selector = '$IM($N)'
|
|
||||||
... adaptor = '~$IM'
|
|
||||||
...
|
|
||||||
>>> SpecialId.selector
|
|
||||||
SpecialId.selector [value='$IM($N)']
|
|
||||||
>>> SpecialId.selector.value
|
|
||||||
'$IM($N)'
|
|
||||||
>>> a = SpecialId.adaptor
|
|
||||||
>>> a == '~$IM'
|
|
||||||
False
|
|
||||||
>>> a == SpecialId.adaptor
|
|
||||||
True
|
|
||||||
>>> print(a)
|
|
||||||
SpecialId.adaptor
|
|
||||||
|
|
||||||
Here ``Enum`` is used to provide readable (and syntactically valid!) names for
|
|
||||||
some special values, as well as group them together.
|
|
||||||
|
|
||||||
While ``Enum`` supports this flexibility, one should only use it in
|
|
||||||
very special cases. Code will be most readable when actual values of
|
|
||||||
enumerations aren't important and enumerations are just used for their
|
|
||||||
naming and comparison properties.
|
|
||||||
|
|
||||||
Enumerations are Python classes, and can have methods and special methods as
|
Enumerations are Python classes, and can have methods and special methods as
|
||||||
usual. If we have this enumeration::
|
usual. If we have this enumeration::
|
||||||
|
@ -295,7 +286,7 @@ usual. If we have this enumeration::
|
||||||
Then::
|
Then::
|
||||||
|
|
||||||
>>> Mood.favorite_mood()
|
>>> Mood.favorite_mood()
|
||||||
Mood.happy [value=3]
|
<Mood.happy: 3>
|
||||||
>>> Mood.happy.describe()
|
>>> Mood.happy.describe()
|
||||||
('happy', 3)
|
('happy', 3)
|
||||||
>>> str(Mood.funky)
|
>>> str(Mood.funky)
|
||||||
|
@ -305,6 +296,7 @@ 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
|
enumeration will become members of this enumeration, with the exception of
|
||||||
*__dunder__* names and descriptors; methods are descriptors too.
|
*__dunder__* names and descriptors; methods are descriptors too.
|
||||||
|
|
||||||
|
|
||||||
Restricted subclassing of enumerations
|
Restricted subclassing of enumerations
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
|
@ -313,19 +305,19 @@ any members. So this is forbidden::
|
||||||
|
|
||||||
>>> class MoreColor(Color):
|
>>> class MoreColor(Color):
|
||||||
... pink = 17
|
... pink = 17
|
||||||
...
|
...
|
||||||
TypeError: Cannot subclass enumerations
|
TypeError: Cannot extend enumerations
|
||||||
|
|
||||||
But this is allowed::
|
But this is allowed::
|
||||||
|
|
||||||
>>> class Foo(Enum):
|
>>> class Foo(Enum):
|
||||||
... def some_behavior(self):
|
... def some_behavior(self):
|
||||||
... pass
|
... pass
|
||||||
...
|
...
|
||||||
>>> class Bar(Foo):
|
>>> class Bar(Foo):
|
||||||
... happy = 1
|
... happy = 1
|
||||||
... sad = 2
|
... sad = 2
|
||||||
...
|
...
|
||||||
|
|
||||||
The rationale for this decision was given by Guido in [6]_. Allowing to
|
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
|
subclass enums that define members would lead to a violation of some
|
||||||
|
@ -334,6 +326,7 @@ makes sense to allow sharing some common behavior between a group of
|
||||||
enumerations, and subclassing empty enumerations is also used to implement
|
enumerations, and subclassing empty enumerations is also used to implement
|
||||||
``IntEnum``.
|
``IntEnum``.
|
||||||
|
|
||||||
|
|
||||||
IntEnum
|
IntEnum
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -388,31 +381,41 @@ replaced with enumerations and backwards compatibility is required
|
||||||
with code that still expects integers.
|
with code that still expects integers.
|
||||||
|
|
||||||
|
|
||||||
Pickling
|
Other derived enumerations
|
||||||
--------
|
--------------------------
|
||||||
|
|
||||||
Enumerations created with the class syntax can also be pickled and unpickled::
|
``IntEnum`` will be part of the ``enum`` module. However, it would be very
|
||||||
|
simple to implement independently::
|
||||||
|
|
||||||
>>> from enum.tests.fruit import Fruit
|
class IntEnum(int, Enum):
|
||||||
>>> from pickle import dumps, loads
|
pass
|
||||||
>>> Fruit.tomato is loads(dumps(Fruit.tomato))
|
|
||||||
True
|
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, mixing types must appear before Enum itself in the
|
||||||
|
sequence of bases.
|
||||||
|
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 behavior-only mixins.
|
||||||
|
|
||||||
|
|
||||||
Convenience API
|
Functional API
|
||||||
---------------
|
--------------
|
||||||
|
|
||||||
The ``Enum`` class is callable, providing the following convenience API::
|
The ``Enum`` class is callable, providing the following functional API::
|
||||||
|
|
||||||
>>> Animal = Enum('Animal', 'ant bee cat dog')
|
>>> Animal = Enum('Animal', 'ant bee cat dog')
|
||||||
>>> Animal
|
>>> Animal
|
||||||
<Enum 'Animal'>
|
<Enum 'Animal'>
|
||||||
>>> Animal.ant
|
>>> Animal.ant
|
||||||
Animal.ant [value=1]
|
<Animal.ant: 1>
|
||||||
>>> Animal.ant.value
|
>>> Animal.ant.value
|
||||||
1
|
1
|
||||||
>>> list(Animal)
|
>>> list(Animal)
|
||||||
[Animal.ant [value=1], Animal.bee [value=2], Animal.cat [value=3], Animal.dog [value=4]]
|
[<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 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
|
||||||
|
@ -429,12 +432,29 @@ equivalent to::
|
||||||
... cat = 3
|
... cat = 3
|
||||||
... dog = 4
|
... dog = 4
|
||||||
|
|
||||||
|
|
||||||
|
Pickling
|
||||||
|
--------
|
||||||
|
|
||||||
|
Enumerations 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, to be importable from that module when unpickling
|
||||||
|
occurs.
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -446,6 +466,7 @@ also explicitly permits subclassing enumerations for extending them with
|
||||||
more members (due to the member/enum separation, the type invariants are not
|
more members (due to the member/enum separation, the type invariants are not
|
||||||
violated in ``flufl.enum`` with such a scheme).
|
violated in ``flufl.enum`` with such a scheme).
|
||||||
|
|
||||||
|
|
||||||
Not having to specify values for enums
|
Not having to specify values for enums
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
|
@ -495,6 +516,7 @@ 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
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
|
@ -528,6 +550,7 @@ 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.
|
||||||
|
|
||||||
|
|
||||||
Acknowledgments
|
Acknowledgments
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
@ -535,6 +558,7 @@ This PEP was initially proposing including the ``flufl.enum`` package [8]_
|
||||||
by Barry Warsaw into the stdlib, and is inspired in large parts by it.
|
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.
|
Ben Finney is the author of the earlier enumeration PEP 354.
|
||||||
|
|
||||||
|
|
||||||
References
|
References
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue