skip to content

What values does enum.auto() assign inside an enum.Flag subclass?

level: middleimportance: should knowfreq 35%

answer

  1. Not the same rule as a plain enum
  2. Every member needs its own bit
  3. Doubling, not counting
  4. Skips past bits already used
  5. A stray 3 is really 1 and 2

basics

~20 s

Inside a Flag or IntFlag subclass, auto() assigns successive powers of two — 1, 2, 4, 8 — rather than the 1, 2, 3 it gives a plain Enum, so every member owns a distinct bit and members can be combined.

solid answer

~40 s

`enum.auto()` asks the enum class for the next value, and `enum.Flag` overrides that rule: instead of "last value plus one" it returns the next power of two above every bit already used. So `READ = auto(); WRITE = auto(); EXEC = auto()` gives 1, 2 and 4, and the members can be OR-ed together without colliding. Writing the numbers by hand is legal but invites a typo like `EXEC = 3`, which is not a third flag at all — it is a *composite alias* for `READ|WRITE`, silently skipped when you iterate the class. `enum.verify(enum.NAMED_FLAGS)` is the guard: it rejects at class-creation time any member whose value covers bits no single member names.

code

python · 17 lines
python
from enum import Flag, auto

class Perm(Flag):
    READ = auto()
    WRITE = auto()
    EXEC = auto()

print([(m.name, m.value) for m in Perm])   # [('READ',1),('WRITE',2),('EXEC',4)]

class Broken(Flag):
    READ = 1
    WRITE = 2
    EXEC = 3          # meant a third flag, wrote a composite

print(Broken.EXEC is (Broken.READ | Broken.WRITE))  # True
print(list(Broken))                                  # only READ and WRITE
print(list(Broken.__members__))                      # includes EXEC

go deeper

for a junior

Remember that auto() inside enum.Flag hands out 1, 2, 4, 8 rather than 1, 2, 3, and that this is what lets members be combined with | without overlapping.

for a middle

Explain the mechanism: Flag overrides the value generator to return the next power of two above all bits used, and be able to say why a hand-written 3 becomes a composite alias instead of a third flag.

for a senior

Show how you prevent the quiet version of this bug in a codebase — @verify(NAMED_FLAGS) on flag enums, auto() over hand numbering, and a test that asserts the member count rather than trusting the class body to be read carefully.

for a principal

Own the convention: whether flag values are an implementation detail or a persisted contract, who is allowed to renumber them, and how a team keeps named bundles readable as the set of flags grows past what one screen shows.

## What `auto()` does `enum.auto()` is a placeholder written in place of a member value. When the class body finishes, the enum machinery calls the class's `_generate_next_value_` for each `auto()` in turn, passing the member's name, the starting value, the count of members defined so far and the list of values already assigned. `enum.Enum`'s implementation returns "the last value plus one", which is why a plain enum numbers 1, 2, 3. `enum.Flag` overrides that rule, because consecutive integers would be useless for a bitfield: 3 is not a new flag, it is 1 and 2 together. `Flag`'s generator returns the **next power of two beyond every bit used so far**: ```python from enum import Flag, auto class Perm(Flag): READ = auto() # 1 WRITE = auto() # 2 EXEC = auto() # 4 ``` Note the exact wording — "beyond every bit used so far", not "double the previous value". If a member is given an explicit composite value, the next `auto()` skips past all of its bits: ```python class Mixed(Flag): A = 1 B = 2 RW = 3 # explicit composite D = auto() # 4, not 8 and not 4 colliding with anything ``` `enum.IntFlag` inherits the same generator, so it numbers identically. ## Why hand-written values are the risk Nothing forces you to use `auto()`; `READ = 1; WRITE = 2; EXEC = 4` is fine. The problem is what happens when someone continues the list by counting rather than doubling: ```python class Perm(Flag): READ = 1 WRITE = 2 EXEC = 3 # the bug ``` This raises nothing. `Perm.EXEC` is created as a *composite alias* — its value 3 is exactly `READ|WRITE`, so `Perm.EXEC is Perm.READ | Perm.WRITE` is `True`, granting EXEC silently grants both of the others, and `Perm.EXEC in perms` is true for anyone who merely has read and write. The failure is quiet in the worst way: the class imports, the tests that only use READ and WRITE pass, and the semantics are wrong. It is also invisible to iteration. Iterating a `Flag` class yields only its **canonical** members — the ones that name a single bit — so `list(Perm)` returns `[READ, WRITE]` and never mentions EXEC, while `Perm.__members__` (the full mapping, aliases included) does. A candidate who tests "did I define three flags?" with `len(list(Perm))` gets 2 and may not know why. ## The guard: `enum.verify(enum.NAMED_FLAGS)` The `enum` module ships a decorator for exactly this class of defect. `enum.verify` takes one or more `enum.EnumCheck` values and runs the check when the class is created: ```python from enum import Flag, verify, NAMED_FLAGS @verify(NAMED_FLAGS) class Perm(Flag): READ = 1 WRITE = 2 EXEC = 5 # ValueError at class creation: bit 0x4 is unnamed ``` `NAMED_FLAGS` requires that every bit appearing in any member's value is also named by some single-bit member. It catches the "5 when you meant 4" slip and the aliasing case above where a composite is defined but no member owns the missing bit. `EnumCheck` also carries `UNIQUE` (no aliases at all) and `CONTINUOUS` (no gaps in the values), which are for ordinary enums rather than flags. Adding `@verify(NAMED_FLAGS)` to a hand-numbered flag enum costs one line and turns a silent semantic bug into an import-time `ValueError`. ## Deliberate named combinations A composite value is not always a mistake — it is the normal way to name a bundle: ```python class Perm(Flag): READ = auto() WRITE = auto() EXEC = auto() RW = READ | WRITE ALL = READ | WRITE | EXEC ``` Here `RW` and `ALL` are intentional aliases. They read well at call sites, they are still skipped by iteration (so nothing double-counts), and `@verify(NAMED_FLAGS)` is happy because every bit they cover is named by a single-bit member. The distinction the interviewer is probing is precisely this: a composite you wrote on purpose and named is documentation; a composite you produced by counting to 3 is a bug. ## Version notes `auto()` and its power-of-two behaviour inside `Flag` are long-standing. `enum.verify`, `enum.EnumCheck` and `NAMED_FLAGS` arrived in Python 3.11, along with the rework that made flag iteration yield canonical members only. On 3.14 all of it behaves as shown.

  • If a member is written as an explicit composite, what value does the next `auto()` produce?
    The next power of two above **every** bit already used, not double the previous value. With `A = 1`, `B = 2`, `RW = 3`, a following `auto()` yields 4, because bits 1 and 2 are taken and 4 is the first free one. That is why the rule is stated as "beyond all bits used" rather than "previous times two".
  • Why does `list(Perm)` not show a member you defined as `RW = READ | WRITE`?
    Iterating a `Flag` class yields only canonical members — those naming exactly one bit — so intentional combinations and aliases are skipped and nothing is counted twice. The full mapping including aliases is `Perm.__members__`, and `Perm.RW` still resolves normally by attribute or by value lookup.
  • Is defining `ALL = READ | WRITE | EXEC` a code smell?
    No, that is the intended way to name a bundle, and `@verify(NAMED_FLAGS)` accepts it because every bit it covers is named by a single-bit member. The smell is a composite produced by accident — counting 1, 2, 3 — which reads as a new flag but silently means two existing ones.

saying these in an interview costs you the question

  • Saying auto() gives 1, 2, 3 inside a Flag subclass
  • Believing a member written as 3 is a distinct third flag
  • Claiming a duplicate-bit member raises at class creation
  • Expecting iteration of a Flag class to include composites
  • Thinking auto() doubles the previous value regardless of gaps

context