In Python 3.11, the same is true for enum.Enum types, but the behavior seems to have changed for enum.Flag types. Now, iterating over the type only yields its nonzero values.
It is more accurate to say that Enum and Flag iterate over the canonical values. For example:
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
ROJO = 1
VERDE = 2
AZUL = 3
list(Color)
# RED, GREEN, BLUE
class BlendColor(Flag):
BLACK = 0
RED = 1
GREEN = 2
BLUE = 4
WHITE = RED | GREEN | BLUE
list(BlendColor)
# RED, GREEN, BLUE
Aliases have always been omitted from iteration, and flag values with no, or more than one, bit(s) set are considered aliases since Python 3.11 -- this was considered a bug-fix.
Oh I see. Thanks for the clarification, @ethanfurman.
Looking back over the docs now, they do pretty much convey everything that you've explained here, although some of the details are subtle. The only thing that I think isn't explicitly explained there is that zero-valued flags are considered aliases.
I do have a use case where I need both alias and elements of a flag. Is there a way to explicitly iterate over either only elements and only aliases that works in both old AND new version?
Activity
CharlieZhao95 commented on Sep 21, 2023
I bisected to here:
sobolevn commented on Sep 21, 2023
From the docs, I can say that now 0 is treated as more like a special value:
https://docs.python.org/3/library/enum.html#enum.Flag
Check how <Color: 0> is used all over the place.
The technical reason behind this is that now 0 does not get into _member_names_ here:
cpython/Lib/enum.py
Lines 313 to 319 in 712cb17
Because 0 is not a _is_single_bit member (it is explicit):
cpython/Lib/enum.py
Lines 93 to 98 in 712cb17
So, when we use __iter__ over _member_names_ zero-value is not there:
cpython/Lib/enum.py
Lines 790 to 794 in 712cb17
We need @ethanfurman :)
ethanfurman commented on Sep 21, 2023
It is more accurate to say that Enum and Flag iterate over the canonical values. For example:
class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 ROJO = 1 VERDE = 2 AZUL = 3 list(Color) # RED, GREEN, BLUE class BlendColor(Flag): BLACK = 0 RED = 1 GREEN = 2 BLUE = 4 WHITE = RED | GREEN | BLUE list(BlendColor) # RED, GREEN, BLUEAliases have always been omitted from iteration, and flag values with no, or more than one, bit(s) set are considered aliases since Python 3.11 -- this was considered a bug-fix.
gmgunter commented on Sep 22, 2023
Oh I see. Thanks for the clarification, @ethanfurman.
Looking back over the docs now, they do pretty much convey everything that you've explained here, although some of the details are subtle. The only thing that I think isn't explicitly explained there is that zero-valued flags are considered aliases.
wimglenn commented on Jun 27, 2024
Why isn't F.f considered to be canonical?
ethanfurman commented on Jun 27, 2024
Because f is not a flag, it is a name for the absence of flags.
wimglenn commented on Jun 27, 2024
f shows up in F.__members__, but not in dir(F). Is that intended?
ethanfurman commented on Jun 28, 2024
Yes. dir(F) doesn't list aliases, but __members__ does (which also prevents aliases from being overridden -- i.e. F.f = 99 raises an AttributeError).
ClundXIII commented on Dec 5, 2024
Hi,
I do have a use case where I need both alias and elements of a flag. Is there a way to explicitly iterate over either only elements and only aliases that works in both old AND new version?
Leo1690 commented on Mar 21, 2025
@ClundXIII You can do this
from enum import IntFlag class Bla(IntFlag): X = 0 list(Bla.__members__.values())