skip to content

Flag and Bitwise Members

Flag and IntFlag let members combine with | and be tested with &, so one value can carry a whole permission set. Interviewers ask how membership is checked and what happens to bits with no member.

part ofPythonoverview, primer and where to startread it →
on this pageshow

questions

4

How do you combine enum.Flag members with | and test one with &?

level: juniorimportance: must knowfreq 45%

answer

  1. One value, several switches
  2. Bitwise operators over members
  3. Union with one operator, mask with another
  4. Result is a member, not an int
  5. Zero-valued member is the only falsy one

basics

~20 s

The | operator unions two enum.Flag members into one composite member carrying both bits, and & intersects them. Test a single flag with perms & Perm.READ, which is truthy only when that bit is set.

solid answer

~40 s

`enum.Flag` gives every member a distinct bit and defines the bitwise operators over members. `Perm.READ | Perm.WRITE` returns another member of the same enum whose value is the OR of the two bits, so one variable can carry a whole set. `&` intersects, `^` toggles, and `~` gives the complement within the bits the class actually names. To ask whether a flag is present, mask it: `if perms & Perm.WRITE:` — the result is a `Flag` member, and the zero-valued member is the only falsy one, so truthiness works as the test. Never compare a composite with `==` against a single member; `perms == Perm.WRITE` is False as soon as any other bit is set. On a plain `Flag` the operands must both be members: `Perm.READ | 4` raises `TypeError`.

code

python · 16 lines
python
from enum import Flag, auto

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

perms = Perm.READ | Perm.WRITE
print(perms)                    # Perm.READ|WRITE
print(perms.value)              # 3
print(bool(perms & Perm.WRITE)) # True
print(bool(perms & Perm.EXEC))  # False
print(perms == Perm.WRITE)      # False - extra bit set

perms &= ~Perm.READ
print(perms)                    # Perm.WRITE

go deeper

for a junior

Recall that enum.Flag members combine with | and are tested with &, and that the result of either is still a member of the enum. Be able to write the grant, revoke and check lines from memory.

for a middle

Explain the mechanics: __or__/__and__/__invert__ return cached composite members, truthiness is defined by the value so the zero member is the only falsy one, and ~ complements only within the named bits.

for a senior

Show judgement about the boundary: plain Flag refuses to mix with raw ints and that strictness is worth keeping internally, while IntFlag is for values that must cross into a database column, a wire format or a system call.

for a principal

Own the question of whether a bitfield is the right representation at all — how many flags the domain will grow to, whether values are persisted and must survive new bits, and what a reviewer six months from now reads when they see an integer in a column.

## What `enum.Flag` actually is `enum.Flag` is a base class in the standard library's `enum` module for enums whose members are meant to be *combined*. An ordinary `enum.Enum` member is a singleton standing for one choice out of several; a `Flag` member stands for one bit, and any subset of those bits is itself a valid value of the enum. That is the whole point: one variable can carry "read and write, but not execute" without a list, a set, or three booleans. ```python from enum import Flag, auto class Perm(Flag): READ = auto() WRITE = auto() EXEC = auto() ``` `auto()` inside a `Flag` subclass assigns successive powers of two — 1, 2, 4 — so the bits never collide. ## The operators `Flag` implements `__or__`, `__and__`, `__xor__` and `__invert__` over its members, and every one of them returns **another member of the same enum**, not a bare integer: ```python rw = Perm.READ | Perm.WRITE rw # <Perm.READ|WRITE: 3> rw & Perm.READ # <Perm.READ: 1> rw & Perm.EXEC # <Perm: 0> rw ^ Perm.READ # <Perm.WRITE: 2> ~Perm.READ # <Perm.WRITE|EXEC: 6> ``` A value that names more than one bit is called a *composite* member. It has a `name` (`'READ|WRITE'`) and a `value` (`3`) like any other member, and it is cached: evaluating `Perm.READ | Perm.WRITE` twice gives you the identical object back, so `is` works on composites too. Note what `~` does. It complements only within the bits the class names — `~Perm.READ` is `WRITE|EXEC`, not the infinite ones-complement of the integer 1. That is what makes `perms & ~Perm.WRITE` a safe way to clear a flag. ## Testing membership by masking The idiomatic presence test is a mask evaluated for truth: ```python if perms & Perm.WRITE: ... ``` This works because `Flag` defines truthiness by value: the only falsy member is the zero-valued one, which is what `&` returns when the bits do not overlap. `Perm(0)` is a real member — it prints as `<Perm: 0>`, iterating it yields nothing, and `bool()` of it is `False`. The mistake to avoid is equality: ```python perms = Perm.READ | Perm.WRITE perms == Perm.WRITE # False — perms carries an extra bit perms & Perm.WRITE # <Perm.WRITE: 2>, truthy ``` Equality asks "is this exactly this set of bits", which is almost never the question being asked. Masking (or the `in` operator, which reads as a subset test) asks "does this set contain that flag", which is. ## Setting and clearing Granting is `|=`, revoking is `&= ~`: ```python perms = Perm.READ perms |= Perm.WRITE # now READ|WRITE perms &= ~Perm.READ # now WRITE ``` Because members are immutable, `|=` rebinds the name to a different member rather than mutating anything — which is exactly what you want if the value is shared. ## `Flag` versus `IntFlag` `enum.Flag` members are **not** integers. Mixing one with a raw `int` is an error: ```python Perm.READ | 4 # TypeError: unsupported operand type(s) for |: 'Perm' and 'int' ``` That strictness is a feature: it stops a stray number from silently becoming a permission. `enum.IntFlag` is the variant that also subclasses `int`, so its members compare equal to their numbers, combine with plain ints, and serialize as ints — which is what you want when the value round-trips through a database column, a wire protocol or an OS-level bitmask, and what you do *not* want when the flags are purely internal. Reach for `Flag` by default and `IntFlag` only where an integer really has to cross a boundary. ## Practical notes All of this has been in `enum` since Python 3.6, but 3.11 reworked the surrounding behaviour: the `repr` of a composite became `<Perm.READ|WRITE: 3>`, composite members became iterable and sized, and the handling of bits no member covers was formalized with the `boundary=` class keyword. On 3.14 everything above holds as written. One last habit: keep the members single-bit. If you want a named combination, define it explicitly (`RW = READ | WRITE`); it becomes an alias for the composite, `Perm.RW is Perm.READ | Perm.WRITE` is `True`, and iterating the class still yields only the three canonical single-bit members. ## Why one flag value instead of three booleans Three separate boolean attributes model the same information, and interviewers sometimes ask why you would not just use them. A `Flag` value is one object to pass, one column or field to store, one thing to log, and it stays correct when a fourth permission is added — the signatures of every function that takes it are unchanged. It also makes the illegal states harder to build: there is no way to hand a function a permission it did not define, whereas three booleans can be passed in the wrong order without a murmur. The cost is indirection, so keep the enum small and named after the domain, not after the bits.

  • Why is `perms == Perm.WRITE` the wrong way to check that write access is granted?
    Equality is an exact-set test. A composite `Flag` member equals only the member with exactly those bits, so as soon as any other permission is present the comparison is `False`. Use a mask (`perms & Perm.WRITE`) or the subset test (`Perm.WRITE in perms`), both of which ask whether the bit is *contained*, which is the real question.
  • What does `~Perm.READ` evaluate to, and why is it not a huge negative number?
    It evaluates to `<Perm.WRITE|EXEC: 6>` — the complement taken only over the bits the enum names, not the two's-complement of the integer 1. `Flag.__invert__` masks the result back into the class's own bit space, which is what makes `perms & ~Perm.READ` a safe idiom for revoking one flag.
  • When would you choose `enum.IntFlag` over `enum.Flag` for a permission set?
    When the value has to leave Python as a number — stored in an integer column, sent over a wire format, or handed to an OS call that expects a bitmask. `IntFlag` members subclass `int`, so they compare equal to their values and combine with raw ints. That same leniency is the reason to prefer plain `Flag` internally: it makes `Perm.READ | 4` a `TypeError` instead of a silent permission.

Think of a keyring rather than a single key: the | operator adds a key to the ring, and & asks whether a particular key is on it.

saying these in an interview costs you the question

  • Comparing a composite flag with == against a single member
  • Claiming | returns a list or set of members
  • Thinking the result of | is a plain int on enum.Flag
  • Expecting Perm.READ | 4 to work on a plain Flag
  • Believing ~ produces a negative two's-complement integer
  • Saying a zero-valued Flag member is truthy

context

open as a page

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

level: middleimportance: should knowfreq 35%

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.

open as a page

What does `Perm.READ in perms` test when perms is an enum.Flag value?

level: middleimportance: should knowfreq 32%

basics

~20 s

It is a subset test: True when every bit of the left operand is set in the composite on the right. Since Python 3.11 a composite Flag member is also iterable and sized, so you can list the single flags it carries.

open as a page

Why does enum.Flag raise on an int carrying bits no member names, while enum.IntFlag keeps them?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Since Python 3.11 each flag class has a boundary policy. enum.Flag defaults to STRICT, so an out-of-range value raises ValueError; enum.IntFlag defaults to KEEP, so unknown bits survive in the value but are invisible to iteration.

open as a page