[Python-checkins] gh-99304: [Enum] clarify what constitutes a flag alias (GH-99395)

ethanfurman webhook-mailer at python.org
Sat Nov 12 13:39:52 EST 2022


https://github.com/python/cpython/commit/73a921b0701a4c135154c14649b49a0bb797e143
commit: 73a921b0701a4c135154c14649b49a0bb797e143
branch: main
author: Ethan Furman <ethan at stoneleaf.us>
committer: ethanfurman <ethan at stoneleaf.us>
date: 2022-11-12T10:39:47-08:00
summary:

gh-99304: [Enum] clarify what constitutes a flag alias (GH-99395)

Co-authored-by: C.A.M. Gerlach <CAM.Gerlach at Gerlach.CAM>

files:
M Doc/howto/enum.rst
M Doc/library/enum.rst

diff --git a/Doc/howto/enum.rst b/Doc/howto/enum.rst
index 5c2fc6f29c0f..98d9f4febe2d 100644
--- a/Doc/howto/enum.rst
+++ b/Doc/howto/enum.rst
@@ -173,6 +173,7 @@ yourself some work and use :func:`auto()` for the values::
     ...     FRIDAY = auto()
     ...     SATURDAY = auto()
     ...     SUNDAY = auto()
+    ...     WEEKEND = SATURDAY | SUNDAY
 
 
 .. _enum-advanced-tutorial:
@@ -305,6 +306,10 @@ Iterating over the members of an enum does not provide the aliases::
 
     >>> list(Shape)
     [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
+    >>> list(Weekday)
+    [<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>]
+
+Note that the aliases ``Shape.ALIAS_FOR_SQUARE`` and ``Weekday.WEEKEND`` aren't shown.
 
 The special attribute ``__members__`` is a read-only ordered mapping of names
 to members.  It includes all names defined in the enumeration, including the
@@ -324,6 +329,11 @@ the enumeration members.  For example, finding all the aliases::
     >>> [name for name, member in Shape.__members__.items() if member.name != name]
     ['ALIAS_FOR_SQUARE']
 
+.. note::
+
+   Aliases for flags include values with multiple flags set, such as ``3``,
+   and no flags set, i.e. ``0``.
+
 
 Comparisons
 -----------
@@ -751,7 +761,7 @@ flags being set, the boolean evaluation is :data:`False`::
     False
 
 Individual flags should have values that are powers of two (1, 2, 4, 8, ...),
-while combinations of flags won't::
+while combinations of flags will not::
 
     >>> class Color(Flag):
     ...     RED = auto()
@@ -1096,8 +1106,8 @@ example of when ``KEEP`` is needed).
 
 .. _enum-class-differences:
 
-How are Enums different?
-------------------------
+How are Enums and Flags different?
+----------------------------------
 
 Enums have a custom metaclass that affects many aspects of both derived :class:`Enum`
 classes and their instances (members).
@@ -1114,6 +1124,13 @@ responsible for ensuring that various other methods on the final :class:`Enum`
 class are correct (such as :meth:`__new__`, :meth:`__getnewargs__`,
 :meth:`__str__` and :meth:`__repr__`).
 
+Flag Classes
+^^^^^^^^^^^^
+
+Flags have an expanded view of aliasing: to be canonical, the value of a flag
+needs to be a power-of-two value, and not a duplicate name.  So, in addition to the
+:class:`Enum` definition of alias, a flag with no value (a.k.a. ``0``) or with more than one
+power-of-two value (e.g. ``3``) is considered an alias.
 
 Enum Members (aka instances)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -1123,9 +1140,35 @@ The most interesting thing about enum members is that they are singletons.
 and then puts a custom :meth:`__new__` in place to ensure that no new ones are
 ever instantiated by returning only the existing member instances.
 
+Flag Members
+^^^^^^^^^^^^
+
+Flag members can be iterated over just like the :class:`Flag` class, and only the
+canonical members will be returned.  For example::
+
+    >>> list(Color)
+    [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
+
+(Note that ``BLACK``, ``PURPLE``, and ``WHITE`` do not show up.)
+
+Inverting a flag member returns the corresponding positive value,
+rather than a negative value --- for example::
+
+    >>> ~Color.RED
+    <Color.GREEN|BLUE: 6>
+
+Flag members have a length corresponding to the number of power-of-two values
+they contain.  For example::
+
+    >>> len(Color.PURPLE)
+    2
+
 
 .. _enum-cookbook:
 
+Enum Cookbook
+-------------
+
 
 While :class:`Enum`, :class:`IntEnum`, :class:`StrEnum`, :class:`Flag`, and
 :class:`IntFlag` are expected to cover the majority of use-cases, they cannot
diff --git a/Doc/library/enum.rst b/Doc/library/enum.rst
index d481f058fbec..74d9e6732762 100644
--- a/Doc/library/enum.rst
+++ b/Doc/library/enum.rst
@@ -27,7 +27,8 @@
 An enumeration:
 
 * is a set of symbolic names (members) bound to unique values
-* can be iterated over to return its members in definition order
+* can be iterated over to return its canonical (i.e. non-alias) members in
+  definition order
 * uses *call* syntax to return members by value
 * uses *index* syntax to return members by name
 
@@ -425,19 +426,23 @@ Data Types
    in most of the same places that a string can be used.  The result of any string
    operation performed on or with a *StrEnum* member is not part of the enumeration.
 
-   .. note:: There are places in the stdlib that check for an exact :class:`str`
-             instead of a :class:`str` subclass (i.e. ``type(unknown) == str``
-             instead of ``isinstance(unknown, str)``), and in those locations you
-             will need to use ``str(StrEnum.member)``.
+   .. note::
+
+      There are places in the stdlib that check for an exact :class:`str`
+      instead of a :class:`str` subclass (i.e. ``type(unknown) == str``
+      instead of ``isinstance(unknown, str)``), and in those locations you
+      will need to use ``str(StrEnum.member)``.
 
    .. note::
 
       Using :class:`auto` with :class:`StrEnum` results in the lower-cased member
       name as the value.
 
-   .. note:: :meth:`__str__` is :func:`str.__str__` to better support the
-      *replacement of existing constants* use-case.  :meth:`__format__` is likewise
-      :func:`str.__format__` for that same reason.
+   .. note::
+
+      :meth:`~object.__str__` is :meth:`!str.__str__` to better support the
+      *replacement of existing constants* use-case.  :meth:`~object.__format__` is likewise
+      :meth:`!str.__format__` for that same reason.
 
    .. versionadded:: 3.11
 
@@ -469,13 +474,17 @@ Data Types
 
    .. method:: __iter__(self):
 
-      Returns all contained members::
+      Returns all contained non-alias members::
 
          >>> list(Color.RED)
          [<Color.RED: 1>]
          >>> list(purple)
          [<Color.RED: 1>, <Color.BLUE: 4>]
 
+      .. versionchanged:: 3.11
+
+         Aliases are no longer returned during iteration.
+
    .. method:: __len__(self):
 
       Returns number of members in flag::
@@ -585,9 +594,15 @@ Data Types
       Using :class:`auto` with :class:`IntFlag` results in integers that are powers
       of two, starting with ``1``.
 
-   .. versionchanged:: 3.11 :meth:`__str__` is now :func:`int.__str__` to
-      better support the *replacement of existing constants* use-case.
-      :meth:`__format__` was already :func:`int.__format__` for that same reason.
+   .. versionchanged:: 3.11
+
+      :meth:`~object.__str__` is now :meth:`!int.__str__` to better support the
+      *replacement of existing constants* use-case.  :meth:`~object.__format__` was
+      already :meth:`!int.__format__` for that same reason.
+
+      Inversion of a :class:`!IntFlag` now returns a positive value that is the
+      union of all flags not in the given flag, rather than a negative value.
+      This matches the existing :class:`Flag` behavior.
 
 .. class:: ReprEnum
 



More information about the Python-checkins mailing list