Issue26988: remove AutoEnum

This commit is contained in:
Ethan Furman 2016-08-20 00:00:52 -07:00
parent 1cce732fcf
commit 332dbc7325
4 changed files with 77 additions and 663 deletions

View file

@ -37,13 +37,6 @@ one decorator, :func:`unique`.
Base class for creating enumerated constants that are also
subclasses of :class:`int`.
.. class:: AutoEnum
Base class for creating automatically numbered members (may
be combined with IntEnum if desired).
.. versionadded:: 3.6
.. function:: unique
Enum class decorator that ensures only one name is bound to any one value.
@ -54,14 +47,14 @@ Creating an Enum
Enumerations are created using the :keyword:`class` syntax, which makes them
easy to read and write. An alternative creation method is described in
`Functional API`_. To define a simple enumeration, subclass :class:`AutoEnum`
as follows::
`Functional API`_. To define an enumeration, subclass :class:`Enum` as
follows::
>>> from enum import AutoEnum
>>> class Color(AutoEnum):
... red
... green
... blue
>>> from enum import Enum
>>> class Color(Enum):
... red = 1
... green = 2
... blue = 3
...
.. note:: Nomenclature
@ -79,33 +72,6 @@ as follows::
are not normal Python classes. See `How are Enums different?`_ for
more details.
To create your own automatic :class:`Enum` classes, you need to add a
:meth:`_generate_next_value_` method; it will be used to create missing values
for any members after its definition.
.. versionadded:: 3.6
If you need full control of the member values, use :class:`Enum` as the base
class and specify the values manually::
>>> from enum import Enum
>>> class Color(Enum):
... red = 19
... green = 7.9182
... blue = 'periwinkle'
...
We'll use the following Enum for the examples below::
>>> class Color(Enum):
... red = 1
... green = 2
... blue = 3
...
Enum Details
------------
Enumeration members have human readable string representations::
>>> print(Color.red)
@ -269,11 +235,7 @@ aliases::
The ``__members__`` attribute can be used for detailed programmatic access to
the enumeration members. For example, finding all the aliases::
>>> [
... name
... for name, member in Shape.__members__.items()
... if member.name != name
... ]
>>> [name for name, member in Shape.__members__.items() if member.name != name]
['alias_for_square']
@ -295,7 +257,7 @@ members are not integers (but see `IntEnum`_ below)::
>>> Color.red < Color.blue
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Color' and 'Color'
TypeError: unorderable types: Color() < Color()
Equality comparisons are defined though::
@ -318,10 +280,10 @@ Allowed members and attributes of enumerations
----------------------------------------------
The examples above use integers for enumeration values. Using integers is
short and handy (and provided by default by :class:`AutoEnum` and the
`Functional 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.
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
the actual value of an enumeration is. But if the value *is* important,
enumerations can have arbitrary values.
Enumerations are Python classes, and can have methods and special methods as
usual. If we have this enumeration::
@ -431,21 +393,17 @@ The :class:`Enum` class is callable, providing the following functional API::
>>> list(Animal)
[<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]
The semantics of this API resemble :class:`~collections.namedtuple`.
The semantics of this API resemble :class:`~collections.namedtuple`. The first
argument of the call to :class:`Enum` is the name of the enumeration.
- the first argument of the call to :class:`Enum` is the name of the
enumeration;
- the second argument is the *source* of enumeration member names. It can be a
whitespace-separated string of names, a sequence of names, a sequence of
2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to
values;
- the last two options enable assigning arbitrary values to enumerations; the
others auto-assign increasing integers starting with 1 (use the ``start``
parameter to specify a different starting value). A new class derived from
:class:`Enum` is returned. In other words, the above assignment to
:class:`Animal` is equivalent to::
The second argument is the *source* of enumeration member names. It can be a
whitespace-separated string of names, a sequence of names, a sequence of
2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to
values. The last two options enable assigning arbitrary values to
enumerations; the others auto-assign increasing integers starting with 1 (use
the ``start`` parameter to specify a different starting value). A
new class derived from :class:`Enum` is returned. In other words, the above
assignment to :class:`Animal` is equivalent to::
>>> class Animal(Enum):
... ant = 1
@ -461,7 +419,7 @@ to ``True``.
Pickling enums created with the functional API can be tricky as frame stack
implementation details are used to try and figure out which module the
enumeration is being created in (e.g. it will fail if you use a utility
function in a separate module, and also may not work on IronPython or Jython).
function in separate module, and also may not work on IronPython or Jython).
The solution is to specify the module name explicitly as follows::
>>> Animal = Enum('Animal', 'ant bee cat dog', module=__name__)
@ -481,15 +439,7 @@ SomeData in the global scope::
The complete signature is::
Enum(
value='NewEnumName',
names=<...>,
*,
module='...',
qualname='...',
type=<mixed-in class>,
start=1,
)
Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
:value: What the new Enum class will record as its name.
@ -525,41 +475,10 @@ The complete signature is::
Derived Enumerations
--------------------
AutoEnum
^^^^^^^^
This version of :class:`Enum` automatically assigns numbers as the values
for the enumeration members, while still allowing values to be specified
when needed::
>>> from enum import AutoEnum
>>> class Color(AutoEnum):
... red
... green = 5
... blue
...
>>> list(Color)
[<Color.red: 1>, <Color.green: 5>, <Color.blue: 6>]
.. note:: Name Lookup
By default the names :func:`property`, :func:`classmethod`, and
:func:`staticmethod` are shielded from becoming members. To enable
them, or to specify a different set of shielded names, specify the
ignore parameter::
>>> class AddressType(AutoEnum, ignore='classmethod staticmethod'):
... pobox
... mailbox
... property
...
.. versionadded:: 3.6
IntEnum
^^^^^^^
Another variation of :class:`Enum` which is also a subclass of
A variation of :class:`Enum` is provided which is also a subclass of
:class:`int`. Members of an :class:`IntEnum` can be compared to integers;
by extension, integer enumerations of different types can also be compared
to each other::
@ -602,13 +521,14 @@ However, they still can't be compared to standard :class:`Enum` enumerations::
>>> [i for i in range(Shape.square)]
[0, 1]
For the vast majority of code, :class:`Enum` and :class:`AutoEnum` are strongly
recommended, since :class:`IntEnum` breaks some semantic promises of an
enumeration (by being comparable to integers, and thus by transitivity to other
unrelated ``IntEnum`` enumerations). It should be used only in special cases
where there's no other choice; for example, when integer constants are replaced
with enumerations and backwards compatibility is required with code that still
expects integers.
For the vast majority of code, :class:`Enum` is strongly recommended,
since :class:`IntEnum` breaks some semantic promises of an enumeration (by
being comparable to integers, and thus by transitivity to other
unrelated enumerations). It should be used only in special cases where
there's no other choice; for example, when integer constants are
replaced with enumerations and backwards compatibility is required with code
that still expects integers.
Others
^^^^^^
@ -620,9 +540,7 @@ simple to implement independently::
pass
This demonstrates how similar derived enumerations can be defined; for example
an :class:`AutoIntEnum` that mixes in :class:`int` with :class:`AutoEnum`
to get members that are :class:`int` (but keep in mind the warnings for
:class:`IntEnum`).
a :class:`StrEnum` that mixes in :class:`str` instead of :class:`int`.
Some rules:
@ -649,35 +567,31 @@ Some rules:
Interesting examples
--------------------
While :class:`Enum`, :class:`AutoEnum`, and :class:`IntEnum` are expected
to cover the majority of use-cases, they cannot cover them all. Here are
recipes for some different types of enumerations that can be used directly,
or as examples for creating one's own.
While :class:`Enum` and :class:`IntEnum` are expected to cover the majority of
use-cases, they cannot cover them all. Here are recipes for some different
types of enumerations that can be used directly, or as examples for creating
one's own.
AutoDocEnum
^^^^^^^^^^^
AutoNumber
^^^^^^^^^^
Automatically numbers the members, and uses the given value as the
:attr:`__doc__` string::
Avoids having to specify the value for each enumeration member::
>>> class AutoDocEnum(Enum):
... def __new__(cls, doc):
>>> class AutoNumber(Enum):
... def __new__(cls):
... value = len(cls.__members__) + 1
... obj = object.__new__(cls)
... obj._value_ = value
... obj.__doc__ = doc
... return obj
...
>>> class Color(AutoDocEnum):
... red = 'stop'
... green = 'go'
... blue = 'what?'
>>> class Color(AutoNumber):
... red = ()
... green = ()
... blue = ()
...
>>> Color.green.value == 2
True
>>> Color.green.__doc__
'go'
.. note::
@ -685,23 +599,6 @@ Automatically numbers the members, and uses the given value as the
members; it is then replaced by Enum's :meth:`__new__` which is used after
class creation for lookup of existing members.
AutoNameEnum
^^^^^^^^^^^^
Automatically sets the member's value to its name::
>>> class AutoNameEnum(Enum):
... def _generate_next_value_(name, start, count, last_value):
... return name
...
>>> class Color(AutoNameEnum):
... red
... green
... blue
...
>>> Color.green.value == 'green'
True
OrderedEnum
^^^^^^^^^^^
@ -834,63 +731,10 @@ member instances.
Finer Points
^^^^^^^^^^^^
Enum class signature
~~~~~~~~~~~~~~~~~~~~
::
class SomeName(
AnEnum,
start=None,
ignore='staticmethod classmethod property',
):
*start* can be used by a :meth:`_generate_next_value_` method to specify a
starting value.
*ignore* specifies which names, if any, will not attempt to auto-generate
a new value (they will also be removed from the class body).
Supported ``__dunder__`` names
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :attr:`__members__` attribute is only available on the class.
:meth:`__new__`, if specified, must create and return the enum members; it is
also a very good idea to set the member's :attr:`_value_` appropriately. Once
all the members are created it is no longer used.
Supported ``_sunder_`` names
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent [class attribute]
- ``_name_`` -- name of the member (but use ``name`` for normal access)
- ``_value_`` -- value of the member; can be set / modified in ``__new__`` (see ``_name_``)
- ``_missing_`` -- a lookup function used when a value is not found (only after class creation)
- ``_generate_next_value_`` -- a function to generate missing values (only during class creation)
:meth:`_generate_next_value_` signature
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``def _generate_next_value_(name, start, count, last_value):``
- ``name`` is the name of the member
- ``start`` is the initital start value (if any) or None
- ``count`` is the number of existing members in the enumeration
- ``last_value`` is the value of the last enum member (if any) or None
Enum member type
~~~~~~~~~~~~~~~~
``Enum`` members are instances of an ``Enum`` class, and even
though they are accessible as ``EnumClass.member``, they should not be accessed
:class:`Enum` members are instances of an :class:`Enum` class, and even
though they are accessible as `EnumClass.member`, they should not be accessed
directly from the member as that lookup may fail or, worse, return something
besides the ``Enum`` member you are looking for::
besides the :class:`Enum` member you looking for::
>>> class FieldTypes(Enum):
... name = 0
@ -904,24 +748,18 @@ besides the ``Enum`` member you are looking for::
.. versionchanged:: 3.5
Boolean value of ``Enum`` classes and members
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Enum classes that are mixed with non-Enum types (such as
Boolean evaluation: Enum classes that are mixed with non-Enum types (such as
:class:`int`, :class:`str`, etc.) are evaluated according to the mixed-in
type's rules; otherwise, all members evaluate as :data:`True`. To make your own
type's rules; otherwise, all members evaluate as ``True``. To make your own
Enum's boolean evaluation depend on the member's value add the following to
your class::
def __bool__(self):
return bool(self.value)
The :attr:`__members__` attribute is only available on the class.
Enum classes with methods
~~~~~~~~~~~~~~~~~~~~~~~~~
If you give your ``Enum`` subclass extra methods, like the `Planet`_
If you give your :class:`Enum` subclass extra methods, like the `Planet`_
class above, those methods will show up in a :func:`dir` of the member,
but not of the class::
@ -929,3 +767,12 @@ but not of the class::
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
>>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']
The :meth:`__new__` method will only be used for the creation of the
:class:`Enum` members -- after that it is replaced. Any custom :meth:`__new__`
method must create the object and set the :attr:`_value_` attribute
appropriately.
If you wish to change how :class:`Enum` members are looked up you should either
write a helper function or a :func:`classmethod` for the :class:`Enum`
subclass.