[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