summaryrefslogtreecommitdiffstats
path: root/Doc/library/enum.rst
diff options
context:
space:
mode:
Diffstat (limited to 'Doc/library/enum.rst')
-rw-r--r--Doc/library/enum.rst394
1 files changed, 359 insertions, 35 deletions
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index a3d5afc..87aa8b1 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -23,9 +23,10 @@ by identity, and the enumeration itself can be iterated over.
Module Contents
---------------
-This module defines two enumeration classes that can be used to define unique
-sets of names and values: :class:`Enum` and :class:`IntEnum`. It also defines
-one decorator, :func:`unique`.
+This module defines four enumeration classes that can be used to define unique
+sets of names and values: :class:`Enum`, :class:`IntEnum`, and
+:class:`IntFlags`. It also defines one decorator, :func:`unique`, and one
+helper, :class:`auto`.
.. class:: Enum
@@ -37,10 +38,27 @@ one decorator, :func:`unique`.
Base class for creating enumerated constants that are also
subclasses of :class:`int`.
+.. class:: IntFlag
+
+ Base class for creating enumerated constants that can be combined using
+ the bitwise operators without losing their :class:`IntFlag` membership.
+ :class:`IntFlag` members are also subclasses of :class:`int`.
+
+.. class:: Flag
+
+ Base class for creating enumerated constants that can be combined using
+ the bitwise operations without losing their :class:`Flag` membership.
+
.. function:: unique
Enum class decorator that ensures only one name is bound to any one value.
+.. class:: auto
+
+ Instances are replaced with an appropriate value for Enum members.
+
+.. versionadded:: 3.6 ``Flag``, ``IntFlag``, ``auto``
+
Creating an Enum
----------------
@@ -57,6 +75,13 @@ follows::
... blue = 3
...
+.. note:: Enum member values
+
+ Member values can be anything: :class:`int`, :class:`str`, etc.. If
+ the exact value is unimportant you may use :class:`auto` instances and an
+ appropriate value will be chosen for you. Care must be taken if you mix
+ :class:`auto` with other values.
+
.. note:: Nomenclature
- The class :class:`Color` is an *enumeration* (or *enum*)
@@ -212,6 +237,42 @@ found :exc:`ValueError` is raised with the details::
ValueError: duplicate values found in <enum 'Mistake'>: four -> three
+Using automatic values
+----------------------
+
+If the exact value is unimportant you can use :class:`auto`::
+
+ >>> from enum import Enum, auto
+ >>> class Color(Enum):
+ ... red = auto()
+ ... blue = auto()
+ ... green = auto()
+ ...
+ >>> list(Color)
+ [<Color.red: 1>, <Color.blue: 2>, <Color.green: 3>]
+
+The values are chosen by :func:`_generate_next_value_`, which can be
+overridden::
+
+ >>> class AutoName(Enum):
+ ... def _generate_next_value_(name, start, count, last_values):
+ ... return name
+ ...
+ >>> class Ordinal(AutoName):
+ ... north = auto()
+ ... south = auto()
+ ... east = auto()
+ ... west = auto()
+ ...
+ >>> list(Ordinal)
+ [<Ordinal.north: 'north'>, <Ordinal.south: 'south'>, <Ordinal.east: 'east'>, <Ordinal.west: 'west'>]
+
+.. note::
+
+ The goal of the default :meth:`_generate_next_value_` methods is to provide
+ the next :class:`int` in sequence with the last :class:`int` provided, but
+ the way it does this is an implementation detail and may change.
+
Iteration
---------
@@ -257,7 +318,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: unorderable types: Color() < Color()
+ TypeError: '<' not supported between instances of 'Color' and 'Color'
Equality comparisons are defined though::
@@ -478,7 +539,7 @@ Derived Enumerations
IntEnum
^^^^^^^
-A variation of :class:`Enum` is provided which is also a subclass of
+The first variation of :class:`Enum` that is provided 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::
@@ -521,13 +582,124 @@ 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` 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.
+
+IntFlag
+^^^^^^^
+
+The next variation of :class:`Enum` provided, :class:`IntFlag`, is also based
+on :class:`int`. The difference being :class:`IntFlag` members can be combined
+using the bitwise operators (&, \|, ^, ~) and the result is still an
+:class:`IntFlag` member. However, as the name implies, :class:`IntFlag`
+members also subclass :class:`int` and can be used wherever an :class:`int` is
+used. Any operation on an :class:`IntFlag` member besides the bit-wise
+operations will lose the :class:`IntFlag` membership.
+
+.. versionadded:: 3.6
+
+Sample :class:`IntFlag` class::
+
+ >>> from enum import IntFlag
+ >>> class Perm(IntFlag):
+ ... R = 4
+ ... W = 2
+ ... X = 1
+ ...
+ >>> Perm.R | Perm.W
+ <Perm.R|W: 6>
+ >>> Perm.R + Perm.W
+ 6
+ >>> RW = Perm.R | Perm.W
+ >>> Perm.R in RW
+ True
+
+It is also possible to name the combinations::
+
+ >>> class Perm(IntFlag):
+ ... R = 4
+ ... W = 2
+ ... X = 1
+ ... RWX = 7
+ >>> Perm.RWX
+ <Perm.RWX: 7>
+ >>> ~Perm.RWX
+ <Perm.-8: -8>
+
+Another important difference between :class:`IntFlag` and :class:`Enum` is that
+if no flags are set (the value is 0), its boolean evaluation is :data:`False`::
+
+ >>> Perm.R & Perm.X
+ <Perm.0: 0>
+ >>> bool(Perm.R & Perm.X)
+ False
+
+Because :class:`IntFlag` members are also subclasses of :class:`int` they can
+be combined with them::
+
+ >>> Perm.X | 8
+ <Perm.8|X: 9>
+
+
+Flag
+^^^^
+
+The last variation is :class:`Flag`. Like :class:`IntFlag`, :class:`Flag`
+members can be combined using the bitwise operators (&, \|, ^, ~). Unlike
+:class:`IntFlag`, they cannot be combined with, nor compared against, any
+other :class:`Flag` enumeration, nor :class:`int`. While it is possible to
+specify the values directly it is recommended to use :class:`auto` as the
+value and let :class:`Flag` select an appropriate value.
+
+.. versionadded:: 3.6
+
+Like :class:`IntFlag`, if a combination of :class:`Flag` members results in no
+flags being set, the boolean evaluation is :data:`False`::
+
+ >>> from enum import Flag
+ >>> class Color(Flag):
+ ... red = auto()
+ ... blue = auto()
+ ... green = auto()
+ ...
+ >>> Color.red & Color.green
+ <Color.0: 0>
+ >>> bool(Color.red & Color.green)
+ False
+
+Individual flags should have values that are powers of two (1, 2, 4, 8, ...),
+while combinations of flags won't::
+
+ >>> class Color(Flag):
+ ... red = auto()
+ ... blue = auto()
+ ... green = auto()
+ ... white = red | blue | green
+ ...
+ >>> Color.white
+ <Color.white: 7>
+
+Giving a name to the "no flags set" condition does not change its boolean
+value::
+
+ >>> class Color(Flag):
+ ... black = 0
+ ... red = auto()
+ ... blue = auto()
+ ... green = auto()
+ ...
+ >>> Color.black
+ <Color.black: 0>
+ >>> bool(Color.black)
+ False
+
+.. note::
+
+ For the majority of new code, :class:`Enum` and :class:`Flag` are strongly
+ recommended, since :class:`IntEnum` and :class:`IntFlag` break some
+ semantic promises of an enumeration (by being comparable to integers, and
+ thus by transitivity to other unrelated enumerations). :class:`IntEnum`
+ and :class:`IntFlag` should be used only in cases where :class:`Enum` and
+ :class:`Flag` will not do; for example, when integer constants are replaced
+ with enumerations, or for interoperability with other systems.
Others
@@ -558,7 +730,8 @@ Some rules:
4. %-style formatting: `%s` and `%r` call the :class:`Enum` class's
:meth:`__str__` and :meth:`__repr__` respectively; other codes (such as
`%i` or `%h` for IntEnum) treat the enum member as its mixed-in type.
-5. :meth:`str.format` (or :func:`format`) will use the mixed-in
+5. :ref:`Formatted string literals <f-strings>`, :meth:`str.format`,
+ and :func:`format` will use the mixed-in
type's :meth:`__format__`. If the :class:`Enum` class's :func:`str` or
:func:`repr` is desired, use the `!s` or `!r` format codes.
@@ -566,18 +739,87 @@ Some rules:
Interesting examples
--------------------
-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.
+While :class:`Enum`, :class:`IntEnum`, :class:`IntFlag`, and :class:`Flag` 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.
+
+
+Omitting values
+^^^^^^^^^^^^^^^
+In many use-cases one doesn't care what the actual value of an enumeration
+is. There are several ways to define this type of simple enumeration:
-AutoNumber
-^^^^^^^^^^
+- use instances of :class:`auto` for the value
+- use instances of :class:`object` as the value
+- use a descriptive string as the value
+- use a tuple as the value and a custom :meth:`__new__` to replace the
+ tuple with an :class:`int` value
-Avoids having to specify the value for each enumeration member::
+Using any of these methods signifies to the user that these values are not
+important, and also enables one to add, remove, or reorder members without
+having to renumber the remaining members.
- >>> class AutoNumber(Enum):
+Whichever method you choose, you should provide a :meth:`repr` that also hides
+the (unimportant) value::
+
+ >>> class NoValue(Enum):
+ ... def __repr__(self):
+ ... return '<%s.%s>' % (self.__class__.__name__, self.name)
+ ...
+
+
+Using :class:`auto`
+"""""""""""""""""""
+
+Using :class:`object` would look like::
+
+ >>> class Color(NoValue):
+ ... red = auto()
+ ... blue = auto()
+ ... green = auto()
+ ...
+ >>> Color.green
+ <Color.green>
+
+
+Using :class:`object`
+"""""""""""""""""""""
+
+Using :class:`object` would look like::
+
+ >>> class Color(NoValue):
+ ... red = object()
+ ... green = object()
+ ... blue = object()
+ ...
+ >>> Color.green
+ <Color.green>
+
+
+Using a descriptive string
+""""""""""""""""""""""""""
+
+Using a string as the value would look like::
+
+ >>> class Color(NoValue):
+ ... red = 'stop'
+ ... green = 'go'
+ ... blue = 'too fast!'
+ ...
+ >>> Color.green
+ <Color.green>
+ >>> Color.green.value
+ 'go'
+
+
+Using a custom :meth:`__new__`
+""""""""""""""""""""""""""""""
+
+Using an auto-numbering :meth:`__new__` would look like::
+
+ >>> class AutoNumber(NoValue):
... def __new__(cls):
... value = len(cls.__members__) + 1
... obj = object.__new__(cls)
@@ -589,8 +831,11 @@ Avoids having to specify the value for each enumeration member::
... green = ()
... blue = ()
...
- >>> Color.green.value == 2
- True
+ >>> Color.green
+ <Color.green>
+ >>> Color.green.value
+ 2
+
.. note::
@@ -730,10 +975,60 @@ member instances.
Finer Points
^^^^^^^^^^^^
-: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 :class:`Enum` member you looking for::
+Supported ``__dunder__`` names
+""""""""""""""""""""""""""""""
+
+:attr:`__members__` is an :class:`OrderedDict` of ``member_name``:``member``
+items. It 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
+""""""""""""""""""""""""""""
+
+- ``_name_`` -- name of the member
+- ``_value_`` -- value of the member; can be set / modified in ``__new__``
+
+- ``_missing_`` -- a lookup function used when a value is not found; may be
+ overridden
+- ``_order_`` -- used in Python 2/3 code to ensure member order is consistent
+ (class attribute, removed during class creation)
+- ``_generate_next_value_`` -- used by the `Functional API`_ and by
+ :class:`auto` to get an appropriate value for an enum member; may be
+ overridden
+
+.. versionadded:: 3.6 ``_missing_``, ``_order_``, ``_generate_next_value_``
+
+To help keep Python 2 / Python 3 code in sync an :attr:`_order_` attribute can
+be provided. It will be checked against the actual order of the enumeration
+and raise an error if the two do not match::
+
+ >>> class Color(Enum):
+ ... _order_ = 'red green blue'
+ ... red = 1
+ ... blue = 3
+ ... green = 2
+ ...
+ Traceback (most recent call last):
+ ...
+ TypeError: member order does not match _order_
+
+.. note::
+
+ In Python 2 code the :attr:`_order_` attribute is necessary as definition
+ order is lost before it can be recorded.
+
+``Enum`` member type
+""""""""""""""""""""
+
+:class:`Enum` members are instances of their :class:`Enum` class, and are
+normally accessed as ``EnumClass.member``. Under certain circumstances they
+can also be accessed as ``EnumClass.member.member``, but you should never do
+this as that lookup may fail or, worse, return something besides the
+:class:`Enum` member you are looking for::
>>> class FieldTypes(Enum):
... name = 0
@@ -747,7 +1042,24 @@ besides the :class:`Enum` member you looking for::
.. versionchanged:: 3.5
-The :attr:`__members__` attribute is only available on the class.
+
+Boolean value of ``Enum`` classes and members
+"""""""""""""""""""""""""""""""""""""""""""""
+
+:class:`Enum` members that are mixed with non-:class:`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 Enum's boolean evaluation depend on the member's value add the following to
+your class::
+
+ def __bool__(self):
+ return bool(self.value)
+
+:class:`Enum` classes always evaluate as :data:`True`.
+
+
+``Enum`` classes with methods
+"""""""""""""""""""""""""""""
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,
@@ -758,11 +1070,23 @@ but not of the class::
>>> 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.
+Combining members of ``Flag``
+"""""""""""""""""""""""""""""
+
+If a combination of Flag members is not named, the :func:`repr` will include
+all named flags and all named combinations of flags that are in the value::
+
+ >>> class Color(Flag):
+ ... red = auto()
+ ... green = auto()
+ ... blue = auto()
+ ... magenta = red | blue
+ ... yellow = red | green
+ ... cyan = green | blue
+ ...
+ >>> Color(3) # named combination
+ <Color.yellow: 3>
+ >>> Color(7) # not named combination
+ <Color.cyan|magenta|blue|yellow|green|red: 7>
+