Skip to content

Flag and IntFlag

Flag and IntFlag are specialized enums for bit flags, supporting bitwise operations like OR, AND, and NOT.

Mental Model

A regular Enum is a radio button -- exactly one option selected at a time. A Flag is a row of checkboxes -- multiple options can be active simultaneously. Each flag member occupies a unique bit position, so combining them with | and testing them with in is fast, unambiguous, and type-safe.

Flag vs Enum

A regular Enum represents one value at a time — a status is either PENDING or ACTIVE, never both. A Flag represents a combination of values (a bitmask) — a user can have READ and WRITE and EXECUTE permissions simultaneously. Use Flag when multiple options can be active at the same time; use Enum when exactly one option is selected.

Why Powers of 2?

Each flag member must be a distinct power of 2 (1, 2, 4, 8, ...). This works because in binary, each power of 2 sets exactly one unique bit:

READ = 1 → 001 WRITE = 2 → 010 EXECUTE = 4 → 100

Combining flags with | (OR) sets multiple bits: READ | WRITE = 011 = 3. Testing with in (AND) checks whether a specific bit is set. If you used consecutive integers (1, 2, 3) instead, the bit patterns would overlap and combinations would be ambiguous — 3 could mean "EXECUTE" or "READ + WRITE."


Flag Basics

```python from enum import Flag, auto

class Permission(Flag): READ = auto() # 1 WRITE = auto() # 2 EXECUTE = auto() # 4

Single permission

user1_perms = Permission.READ print(user1_perms) # Permission.READ

Multiple permissions with bitwise OR

user2_perms = Permission.READ | Permission.WRITE print(user2_perms) # Permission.READ|WRITE

Check if permission is set

print(Permission.READ in user2_perms) # True print(Permission.EXECUTE in user2_perms) # False ```

Practical Flag Example

```python from enum import Flag, auto

class FileMode(Flag): READABLE = auto() # 1 WRITABLE = auto() # 2 EXECUTABLE = auto() # 4

def check_permissions(mode: FileMode, required: FileMode) -> bool: return required in mode

file_mode = FileMode.READABLE | FileMode.WRITABLE

Check single permission

print(check_permissions(file_mode, FileMode.READABLE)) # True print(check_permissions(file_mode, FileMode.EXECUTABLE)) # False

Check multiple permissions

can_read_write = FileMode.READABLE | FileMode.WRITABLE print(can_read_write in file_mode) # True ```

IntFlag

```python from enum import IntFlag, auto

class Status(IntFlag): RUNNING = auto() # 1 PAUSED = auto() # 2 STOPPED = auto() # 4 ERROR = auto() # 8

IntFlag can also use arithmetic

status = Status.RUNNING | Status.ERROR print(status) # Status.RUNNING|ERROR print(status.value) # 9 (1 + 8)

Can mix with integers

combined = status | 0x10 # Add raw bits print(combined.value) # 25

Bitwise operations work

print(status & Status.RUNNING) # Status.RUNNING print(status & Status.PAUSED) # Status(0) - empty ```

Flag Iteration and Checking

```python from enum import Flag, auto

class Feature(Flag): FEATURE_A = auto() FEATURE_B = auto() FEATURE_C = auto() FEATURE_D = auto()

enabled_features = Feature.FEATURE_A | Feature.FEATURE_C

Iterate over enabled features

print("Enabled features:") for feature in Feature: if feature in enabled_features: print(f" - {feature.name}")

Count enabled features

count = bin(enabled_features.value).count('1') print(f"Total enabled: {count}") ```

Combining Flags

```python from enum import Flag, auto

class DatabaseOption(Flag): CACHE = auto() # 1 REPLICATE = auto() # 2 COMPRESS = auto() # 4 ENCRYPT = auto() # 8 VALIDATE = auto() # 16

def create_connection(options: DatabaseOption): if DatabaseOption.ENCRYPT in options: print("Enabling encryption") if DatabaseOption.CACHE in options: print("Enabling cache") if DatabaseOption.REPLICATE in options: print("Enabling replication")

Create options combination

conn_options = (DatabaseOption.ENCRYPT | DatabaseOption.CACHE | DatabaseOption.REPLICATE)

create_connection(conn_options) ```

Removing Flags

```python from enum import Flag, auto

class Permission(Flag): READ = auto() WRITE = auto() DELETE = auto() ADMIN = auto()

Start with multiple permissions

perms = Permission.READ | Permission.WRITE | Permission.DELETE

Remove a permission

perms = perms & ~Permission.DELETE # Bitwise NOT and AND print(perms) # Permission.READ|WRITE

Check it's gone

print(Permission.DELETE in perms) # False ```

IntFlag Arithmetic

```python from enum import IntFlag, auto

class Level(IntFlag): BASIC = auto() # 1 INTERMEDIATE = auto() # 2 ADVANCED = auto() # 4

level = Level.INTERMEDIATE print(level.value) # 2

IntFlag supports arithmetic

next_level = level | Level.ADVANCED print(next_level.value) # 6

Can extract individual flags

remaining = next_level & ~Level.INTERMEDIATE print(remaining) # Level.ADVANCED ```

When to Use Flag

  • Permissions and capabilities
  • Feature toggles
  • Configuration options
  • Combination of boolean settings
  • Need bitwise operations

Exercises

Exercise 1. Create a FilePermission Flag with READ, WRITE, EXECUTE. Write a function check_access(user_perms, required_perms) that returns True if the user has all required permissions. Test with various combinations.

Solution to Exercise 1
from enum import Flag, auto

class FilePermission(Flag):
    READ = auto()
    WRITE = auto()
    EXECUTE = auto()

def check_access(user_perms, required_perms):
    return required_perms in user_perms

user = FilePermission.READ | FilePermission.WRITE
print(check_access(user, FilePermission.READ))                # True
print(check_access(user, FilePermission.EXECUTE))             # False
print(check_access(user, FilePermission.READ | FilePermission.WRITE))  # True

Exercise 2. Define a Feature Flag with DARK_MODE, NOTIFICATIONS, ANALYTICS, BETA. Write a function toggle_feature(current, feature) that flips a feature on or off (using XOR ^). Start with no features enabled and toggle several features on and off.

Solution to Exercise 2
from enum import Flag, auto

class Feature(Flag):
    DARK_MODE = auto()
    NOTIFICATIONS = auto()
    ANALYTICS = auto()
    BETA = auto()

def toggle_feature(current, feature):
    return current ^ feature

features = Feature(0)  # No features
features = toggle_feature(features, Feature.DARK_MODE)
print(features)  # Feature.DARK_MODE

features = toggle_feature(features, Feature.NOTIFICATIONS)
print(features)  # Feature.DARK_MODE|NOTIFICATIONS

features = toggle_feature(features, Feature.DARK_MODE)  # Toggle off
print(features)  # Feature.NOTIFICATIONS

Exercise 3. Create an IntFlag called StatusCode with RUNNING = 1, PAUSED = 2, ERROR = 4, MAINTENANCE = 8. Demonstrate that IntFlag values can be combined with plain integers using |. Show that status.value returns the numeric sum and that individual flags can be extracted using &.

Solution to Exercise 3
from enum import IntFlag, auto

class StatusCode(IntFlag):
    RUNNING = 1
    PAUSED = 2
    ERROR = 4
    MAINTENANCE = 8

status = StatusCode.RUNNING | StatusCode.ERROR
print(status)         # StatusCode.RUNNING|ERROR
print(status.value)   # 5 (1 + 4)

# Can mix with integers
combined = status | 0x10
print(combined.value)  # 21

# Extract individual flags
print(status & StatusCode.RUNNING)  # StatusCode.RUNNING
print(status & StatusCode.PAUSED)   # StatusCode(0) — not set

Exercise 4. Explain why the following Flag definition is broken. What value does ADMIN get, and why does combining READ | WRITE produce an ambiguous result?

```python from enum import Flag

class BadPermission(Flag): READ = 1 WRITE = 2 EXECUTE = 3 # NOT a power of 2! ADMIN = 4 ```

Solution to Exercise 4

EXECUTE = 3 is 011 in binary — the same bit pattern as READ | WRITE (001 | 010 = 011). This means there is no way to tell whether a combined value of 3 means "EXECUTE alone" or "READ + WRITE together."

ADMIN = 4 (100) is fine because it is a power of 2 and occupies its own unique bit.

The fix is to use powers of 2 for every member:

class Permission(Flag):
    READ = 1       # 001
    WRITE = 2      # 010
    EXECUTE = 4    # 100
    ADMIN = 8      # 1000

Alternatively, use auto() which assigns powers of 2 automatically for Flag enums. The rule is simple: every flag member must set exactly one bit that no other member sets.


Exercise 5. Create a Flag enum Weekday with members for each day of the week (MON through SUN). Define composite members WEEKDAYS = MON | TUE | WED | THU | FRI and WEEKEND = SAT | SUN. Write a function is_workday(day: Weekday) -> bool that checks whether a single day is in WEEKDAYS. Demonstrate with several days.

Solution to Exercise 5
from enum import Flag, auto

class Weekday(Flag):
    MON = auto()
    TUE = auto()
    WED = auto()
    THU = auto()
    FRI = auto()
    SAT = auto()
    SUN = auto()
    WEEKDAYS = MON | TUE | WED | THU | FRI
    WEEKEND = SAT | SUN

def is_workday(day: Weekday) -> bool:
    return day in Weekday.WEEKDAYS

print(is_workday(Weekday.MON))  # True
print(is_workday(Weekday.SAT))  # False
print(is_workday(Weekday.FRI))  # True
print(is_workday(Weekday.SUN))  # False

# Composite members work too
print(Weekday.WEEKEND)  # Weekday.SAT|SUN