Python IntFlag: Named Bitwise Flags
python intflag: Learn how to use Python's IntFlag enum class to define named bitwise flags, combine them, and test membership with type safety.
When you need to represent a set of boolean options in Python, you can reach for an integer and assign each option a bit position. That works, but it leaves you with magic numbers scattered through the code. Python's enum.IntFlag gives you named constants that still behave like integers, so you get readable code without losing the ability to use them in bitwise operations. This article explains how python intflag works, where it differs from Flag, and how to use it effectively in real code.
What IntFlag Provides Over Plain Integers
Using plain integers for flags means every check involves a bitwise operator and a numeric literal. For example, if permissions & 4: tells the reader nothing about what 4 represents. IntFlag solves this by letting you define a set of named members, each with a value that is a power of two. The resulting objects are subclasses of int, so they can be used anywhere an integer is expected, including arithmetic, comparisons, and serialization.
The key advantage is that you get both the readability of an enum and the compactness of a bitmask. Instead of writing permissions = 7 and later trying to remember which bits are set, you write permissions = Permissions.READ | Permissions.WRITE | Permissions.EXECUTE. The intent is explicit, and the value is still an integer that can be stored in a database or sent over a wire.
Defining an IntFlag Enum
To create an IntFlag, subclass enum.IntFlag and assign members with values that are powers of two. The enum module will automatically handle the bitwise operations for you.
from enum import IntFlag class Permissions(IntFlag): READ = 1 WRITE = 2 EXECUTE = 4
Each member is an instance of Permissions and also an int. The values must be distinct powers of two; otherwise, the enum will raise a ValueError because it cannot represent a unique combination. You can also use the auto() helper, but it increments by one, so you must explicitly set the values or use auto() with a custom _generate_next_value_ method that returns powers of two. For most cases, explicit values are clearer.
Combining and Testing Flags
Once you have an IntFlag class, you can combine members with the bitwise OR operator |, and test membership with the bitwise AND operator &. The result of a combination is also an IntFlag instance, not a plain integer.
perms = Permissions.READ | Permissions.WRITE print(perms) # Permissions.READ|WRITE if perms & Permissions.READ: print("can read") if perms & Permissions.EXECUTE: print("can execute") # not printed
The in operator works as a shorthand for & when you want to check if a single flag is set. bool(perms) returns True if any bit is set, which is useful in conditional statements. Because IntFlag is an int, you can also use it in arithmetic, though that is rarely needed.
IntFlag vs Flag: When the Integer Behavior Matters
Python's enum module also provides Flag, which behaves like IntFlag but does not inherit from int. The choice between them depends on whether you need integer compatibility.
| Feature | IntFlag | Flag |
|---|---|---|
Inherits from int | Yes | No |
| Can be used in integer contexts | Yes | No |
| Can be compared to plain integers | Yes | No |
| Serializes to integer automatically | Yes | Requires .value |
| Type safety | Good | Stronger (no accidental mixing with ints) |
Use IntFlag when you need to pass flags to functions that expect integer bitmasks, such as os.open() or a C extension. Use Flag when you want to enforce that only defined members are used and you don't need integer conversion. In practice, IntFlag is more common because it offers the same bitwise convenience while remaining compatible with existing integer APIs.
Handling Unknown Flag Combinations
An IntFlag instance can hold any combination of bits, not just those that correspond to defined members. For example, Permissions(8) is valid even if no member has value 8. This is intentional and mirrors how bitmasks work. When you iterate over an IntFlag class, you only get the defined members, so you can check which defined flags are present in a combined value.
combined = Permissions.READ | Permissions.WRITE for flag in Permissions: if flag in combined: print(f"{flag.name} is set")
If you need to handle unknown bits, you can override _missing_ to return a custom instance or None. This is useful when you receive flags from an external system and want to log or reject unrecognized bits. Keep in mind that IntFlag values are not limited to powers of two; you can create a member with value 3, but that would overlap with READ and WRITE, making it ambiguous. Stick to powers of two for individual members.
Performance and Memory Considerations
IntFlag instances are Python objects, so they carry a small overhead compared to raw integers. For most applications, this overhead is negligible. The real cost appears when you perform many bitwise operations in a tight loop, because each operation creates a new IntFlag object. If you are working with millions of operations, you might see a measurable difference. In such cases, you can fall back to plain integers and convert to IntFlag only at the boundaries of your code.
Memory usage is also slightly higher than a plain integer, but again, this matters only when you store a large number of flags. The benefit of readability and type safety usually outweighs the cost. If you need to serialize flags to a compact format, you can use .value to get the integer and reconstruct the IntFlag later with Permissions(value).
Common Pitfalls and How to Avoid Them
The most frequent mistake is assigning non-power-of-two values to members. This causes unexpected behavior when combining flags, because overlapping bits make it impossible to distinguish individual members. Always use values like 1, 2, 4, 8, and so on.
Another pitfall is mixing IntFlag with plain integers in a way that loses type information. For example, Permissions.READ | 4 returns an IntFlag because the left operand is an IntFlag, but 4 | Permissions.READ returns a plain integer. This asymmetry can lead to subtle bugs. To avoid it, always start with an IntFlag operand or explicitly convert with Permissions(value).
Finally, be careful when using IntFlag in a boolean context. An empty combination, such as Permissions(0), evaluates to False, which is usually what you want. But a combined value with only the first bit set also evaluates to True, so you must check specific flags rather than relying on truthiness alone.
Extending IntFlag with Custom Methods
You can add methods to an IntFlag class to encapsulate domain logic. For example, you might want a method that returns a human-readable list of active flags, or one that validates whether a given combination is allowed.
class Permissions(IntFlag): READ = 1 WRITE = 2 EXECUTE = 4 def names(self): return [flag.name for flag in type(self) if flag in self] @classmethod def from_names(cls, names): return cls(0) | sum(getattr(cls, name) for name in names)
These methods make the flag type self-documenting and reduce duplication across the codebase. Because IntFlag is an int, you can also use it directly in functions that expect integer flags, such as os.open(). For example, you could define an OpenFlags class that maps to the constants in the os module, giving you named flags for file open modes while still passing the integer value to the system call. This pattern is useful when you want to keep your code readable without sacrificing compatibility with lower-level APIs.