[Python-checkins] peps: Updated PEP 435:
eli.bendersky
python-checkins at python.org
Thu Apr 4 15:52:34 CEST 2013
http://hg.python.org/peps/rev/513ec334a4b1
changeset: 4832:513ec334a4b1
user: Eli Bendersky <eliben at gmail.com>
date: Thu Apr 04 06:52:14 2013 -0700
summary:
Updated PEP 435:
* split the main section to some subsections, to make it more readable
* described non-int values
* removed mentions of implicit int-ness of Enum, which is no longer
supported (unless in IntEnum)
* described the new convenience API
* misc. cleanups
files:
pep-0435.txt | 344 ++++++++++++++++++++------------------
1 files changed, 179 insertions(+), 165 deletions(-)
diff --git a/pep-0435.txt b/pep-0435.txt
--- a/pep-0435.txt
+++ b/pep-0435.txt
@@ -20,11 +20,9 @@
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 integer
-values. Within an enumeration, the values can be compared by identity, and the
-enumeration itself can be iterated over. Enumeration items can be converted to
-and from their integer equivalents, supporting use cases such as storing
-enumeration values in a database.
+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
+enumeration itself can be iterated over.
Decision
@@ -109,17 +107,6 @@
... green = 2
... blue = 3
-Enumeration values are compared by identity::
-
- >>> Colors.red is Colors.red
- True
- >>> Colors.blue is Colors.blue
- True
- >>> Colors.red is not Colors.blue
- True
- >>> Colors.blue is Colors.red
- False
-
Enumeration values have nice, human readable string representations::
>>> print(Colors.red)
@@ -161,6 +148,107 @@
>>> print(repr(Colors))
<Colors {red: 1, green: 2, blue: 3}>
+The ``Enum`` class supports iteration. The returned order is not guaranteed
+(unless you use `IntEnum`_)::
+
+ >>> [v.name for v in MoreColors]
+ ['red', 'green', 'blue', 'pink', 'cyan']
+
+Enumeration values are hashable, so they can be used in dictionaries and sets::
+
+ >>> apples = {}
+ >>> apples[Colors.red] = 'red delicious'
+ >>> apples[Colors.green] = 'granny smith'
+ >>> apples
+ {<EnumValue: Colors.green [value=2]>: 'granny smith', <EnumValue: Colors.red [value=1]>: 'red delicious'}
+
+You can convert back to the enumeration value by indexing into the
+``Enum`` subclass, passing in the integer value for the item you want::
+
+ >>> Colors[1]
+ <EnumValue: Colors.red [int=1]>
+ >>> Colors[2]
+ <EnumValue: Colors.green [int=2]>
+ >>> Colors[3]
+ <EnumValue: Colors.blue [int=3]>
+ >>> Colors[1] is Colors.red
+ True
+
+The string name of the enumeration value is also accepted::
+
+ >>> Colors['red']
+ <EnumValue: Colors.red [int=1]>
+ >>> Colors['blue'] is Colors.blue
+ True
+
+You get exceptions though, if you try to use invalid arguments::
+
+ >>> Colors['magenta']
+ Traceback (most recent call last):
+ ...
+ ValueError: magenta
+ >>> Colors[99]
+ Traceback (most recent call last):
+ ...
+ ValueError: 99
+
+
+Comparisons
+-----------
+
+Enumeration values are compared by identity::
+
+ >>> Colors.red is Colors.red
+ True
+ >>> Colors.blue is Colors.blue
+ True
+ >>> Colors.red is not Colors.blue
+ True
+ >>> Colors.blue is Colors.red
+ False
+
+Ordered comparisons between enumeration values are *not* supported. Enums are
+not integers (but see `IntEnum`_ below)::
+
+ >>> Colors.red < Colors.blue
+ Traceback (most recent call last):
+ ...
+ NotImplementedError
+ >>> 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::
+
+ >>> Colors.blue == Colors.blue
+ True
+ >>> Colors.green != Colors.blue
+ True
+
+Comparisons against non-enumeration values will always compare not equal::
+
+ >>> Colors.green == 2
+ False
+ >>> Colors.blue == 3
+ False
+ >>> Colors.green != 3
+ True
+ >>> Colors.green == 'green'
+ False
+
+
+Extending enumerations by subclassing
+-------------------------------------
+
You can extend previously defined Enums by subclassing::
>>> class MoreColors(Colors):
@@ -195,112 +283,7 @@
>>> len(set((Colors.red, OtherColors.red)))
2
-Ordered comparisons between enumeration values are *not* supported. Enums are
-not integers (but see ``IntEnum`` below)::
-
- >>> Colors.red < Colors.blue
- Traceback (most recent call last):
- ...
- NotImplementedError
- >>> 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::
-
- >>> Colors.blue == Colors.blue
- True
- >>> Colors.green != Colors.blue
- True
-
-Enumeration values do not support ordered comparisons::
-
- >>> Colors.red < Colors.blue
- Traceback (most recent call last):
- ...
- NotImplementedError
- >>> Colors.red < 3
- Traceback (most recent call last):
- ...
- NotImplementedError
- >>> Colors.red <= 3
- Traceback (most recent call last):
- ...
- NotImplementedError
- >>> Colors.blue > 2
- Traceback (most recent call last):
- ...
- NotImplementedError
- >>> Colors.blue >= 2
- Traceback (most recent call last):
- ...
- NotImplementedError
-
-While equality comparisons are allowed, comparisons against non-enumeration
-values will always compare not equal::
-
- >>> Colors.green == 2
- False
- >>> Colors.blue == 3
- False
- >>> Colors.green != 3
- True
- >>> Colors.green == 'green'
- False
-
-If you really want the integer equivalent values, you can convert enumeration
-values explicitly using the ``int()`` built-in. This is quite convenient for
-storing enums in a database, as well as for interoperability with C extensions
-that expect integers::
-
- >>> int(Colors.red)
- 1
- >>> int(Colors.green)
- 2
- >>> int(Colors.blue)
- 3
-
-You can also convert back to the enumeration value by indexing into the Enum
-subclass, passing in the integer value for the item you want::
-
- >>> Colors[1]
- <EnumValue: Colors.red [int=1]>
- >>> Colors[2]
- <EnumValue: Colors.green [int=2]>
- >>> Colors[3]
- <EnumValue: Colors.blue [int=3]>
- >>> Colors[1] is Colors.red
- True
-
-The string name of the enumeration value is also accepted::
-
- >>> Colors['red']
- <EnumValue: Colors.red [int=1]>
- >>> Colors['blue'] is Colors.blue
- True
-
-You get exceptions though, if you try to use invalid arguments::
-
- >>> Colors['magenta']
- Traceback (most recent call last):
- ...
- ValueError: magenta
- >>> Colors[99]
- Traceback (most recent call last):
- ...
- ValueError: 99
-
-The integer equivalent values serve another purpose. You may not define two
-enumeration values with the same integer value::
+You may not define two enumeration values with the same integer value::
>>> class Bad(Enum):
... cartman = 1
@@ -321,23 +304,42 @@
...
TypeError: Multiple enum values: 2
-The Enum class support iteration. Enumeration values are returned in the
-sorted order of their integer equivalent values::
- >>> [v.name for v in MoreColors]
- ['red', 'green', 'blue', 'pink', 'cyan']
- >>> [int(v) for v in MoreColors]
- [1, 2, 3, 4, 5]
+Enumeration values
+------------------
-Enumeration values are hashable, so they can be used in dictionaries and sets::
+The examples above use integers for enumeration values. Using integers is
+short and handy (and provided by default by the `Convenience API`_), but not
+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,
+enumerations can have arbitrary values. The following example uses strings::
- >>> apples = {}
- >>> apples[Colors.red] = 'red delicious'
- >>> apples[Colors.green] = 'granny smith'
- >>> for color in sorted(apples, key=int):
- ... print(color.name, '->', apples[color])
- red -> red delicious
- green -> granny smith
+ >>> class SpecialId(Enum):
+ ... selector = '$IM($N)'
+ ... adaptor = '~$IM'
+ ...
+ >>> SpecialId.selector
+ <EnumValue: SpecialId.selector [value=$IM($N)]>
+ >>> SpecialId.selector.value
+ '$IM($N)'
+ >>> a = SpecialId.adaptor
+ >>> a == '~$IM'
+ False
+ >>> a == SpecialId.adaptor
+ True
+ >>> print(a)
+ SpecialId.adaptor
+ >>> print(a.value)
+ ~$IM
+
+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.
+
IntEnum
-------
@@ -377,6 +379,15 @@
>>> Shape.circle == Colors.red
False
+``IntEnum`` behaves like an integer in other ways you'd expect::
+
+ >>> int(Shape.circle)
+ 1
+ >>> ['a', 'b', 'c'][Shape.circle]
+ 'b'
+ >>> [i for i in range(Shape.square)]
+ [0, 1]
+
For the vast majority of code, ``Enum`` is strongly recommended. Since
``IntEnum`` breaks some semantic promises of an enumeration (by being comparable
to integers, and thus by transitivity to other unrelated enumerations), it
@@ -384,6 +395,7 @@
when integer constants are replaced with enumerations and backwards
compatibility is required with code that still expects integers.
+
Pickling
--------
@@ -394,36 +406,42 @@
>>> Fruit.tomato is loads(dumps(Fruit.tomato))
True
+
Convenience API
---------------
-TODO: update to the new convenience API
+The ``Enum`` class is callable, providing the following convenience API::
-You can also create enumerations using the convenience function ``make()``,
-which takes an iterable object or dictionary to provide the item names and
-values. ``make()`` is a module-level function.
+ >>> Animals = Enum('Animals', 'ant bee cat dog')
+ >>> Animals
+ <Animals {ant: 1, bee: 2, cat: 3, dog: 4}>
+ >>>
+ >>> Animals.ant
+ <EnumValue: Animals.ant [value=1]>
+ >>> Animals.ant.value
+ 1
-The first argument to ``make()`` is the name of the enumeration, and it returns
-the so-named `Enum` subclass. The second argument is a *source* which can be
-either an iterable or a dictionary. In the most basic usage, *source* returns
-a sequence of strings which name the enumeration items. In this case, the
-values are automatically assigned starting from 1::
+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
+a source of enumeration value names. It can be a whitespace-separated string
+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
+auto-assign increasing integers starting with 1. A new class derived from
+``Enum`` is returned. In other words, the above assignment to ``Animals`` is
+equivalent to::
- >>> import enum
- >>> enum.make('Animals', ('ant', 'bee', 'cat', 'dog'))
+ >>> class Animals(Enum):
+ ... ant = 1
+ ... bee = 2
+ ... cat = 3
+ ... dog = 4
+
+Examples of alternative name/value specifications::
+
+ >>> Enum('Animals', ['ant', 'bee', 'cat', 'dog'])
<Animals {ant: 1, bee: 2, cat: 3, dog: 4}>
-
-The items in source can also be 2-tuples, where the first item is the
-enumeration value name and the second is the integer value to assign to the
-value. If 2-tuples are used, all items must be 2-tuples::
-
- >>> def enumiter():
- ... start = 1
- ... while True:
- ... yield start
- ... start <<= 1
- >>> enum.make('Flags', zip(list('abcdefg'), enumiter()))
- <Flags {a: 1, b: 2, c: 4, d: 8, e: 16, f: 32, g: 64}>
+ >>> Enum('Animals', (('ant', 'one'), ('bee', 'two'), ('cat', 'three'), ('dog', 'four')))
+ <Animals {dog: four, ant: one, cat: three, bee: two}>
Proposed variations
@@ -432,6 +450,7 @@
Some variations were proposed during the discussions in the mailing list.
Here's some of the more popular ones.
+
Not having to specify values for enums
--------------------------------------
@@ -450,6 +469,7 @@
definition of such enums baffling when first seen. Besides, explicit is
better than implicit.
+
Using special names or forms to auto-assign enum values
-------------------------------------------------------
@@ -571,12 +591,6 @@
====
* Mark PEP 354 "superseded by" this one, if accepted
- * New package name within stdlib - enum? (top-level)
- * For make, can we add an API like namedtuple's?
- make('Animals, 'ant bee cat dog')
- I.e. when make sees a string argument it splits it, making it similar to a
- tuple but with far less manual quote typing. OTOH, it just saves a ".split"
- so may not be worth the effort ?
..
Local Variables:
--
Repository URL: http://hg.python.org/peps
More information about the Python-checkins
mailing list