Sitemap

Bitwise in Python

3 min readApr 12, 2026

--

Introduction

I’ve always had a soft spot for storing configuration using a bitwise approach.

Press enter or click to view image in full size

Bitmasks in configuration systems go all the way back to early UNIX systems. It’s actually hard to pinpoint when this pattern truly began — after all, early computers relied on physically toggling bits to define configuration.

Get Wojciech Nowicki’s stories in your inbox

Join Medium for free to get updates from this writer.

In modern high-level languages, this approach is far less common. Knowledge of bitwise operations is no longer something every developer can be assumed to have. That’s why it’s worth starting with a short theoretical refresher.

Theory

Bits

Every byte consists of 8 bits — the smallest units of information, each holding a value of 0 or 1. Bitwise operations allow us to work directly on these bits, which brings several advantages:

  • Memory efficiency — a single integer can store 32 or even 64 boolean flags;
  • Performance — operations like AND, OR, XOR, or bit shifts are executed in a single CPU cycle;
  • Simplicity — instead of managing multiple boolean variables, we keep a consistent set of flags in a single field.

You’ll encounter this approach in Unix file permissions, networking protocols, and computer graphics, among others.

Bitwise operators

| Operator | Name        | Example (`a = 0b0101`, `b = 0b0011`)  | Result (`bin(...)`)|
| -------- | ----------- | ------------------------------------- | ------------------ |
| `&` | AND | `a & b` | `0b0001` |
| `|` | OR | `a|b` | `0b0111` |
| `^` | XOR | `a ^ b` | `0b0110` |
| `~` | NOT | `~a` (na 32 bitach) | `0b…111010` |
| `<<` | shift left | `a << 1` | `0b1010` |
| `>>` | shift right | `a >> 2` | `0b0001` |

Bit flags

  • Each flag is assigned a power of two: 1, 2, 4, 8, …
  • This ensures every flag occupies a unique bit and can coexist independently
  • A set of active flags is stored in a single integer

A “raw” example:

READ  = 1  # 0b001
WRITE = 2 # 0b010
EXEC = 4 # 0b100

perm = READ | WRITE # enable two flags
can_exec = bool(perm & EXEC) # check EXEC
perm &= ~WRITE # disable WRITE

This works — but it’s easy to make mistakes (magic numbers) and lacks a clean, expressive API.

The BitAware library

BitAware is my attempt to simplify working with bit flags. Instead of dealing with raw integers, it wraps the underlying mechanism in a typed, readable interface:

  • BitFlag — define a set of flags similarly to enum.IntFlag, but without manually assigning values
  • BitAware[T] — a container class that stores a combination of flags of type T and provides helper methods (has, etc.)
  • Seamless integration with Pydantic — model fields automatically convert between int and flag representations

Installation

Using uv:

uv add bitaware

Or with pip:

pip install bitaware

Declaration

from bitaware import BitFlag, BitAware

class PermissionTypes(BitFlag):
ADMIN = 1
USER = 2
GUEST = 4
MODERATOR = 8
class UserPermission(BitAware[PermissionTypes]):
def __init__(self, value: int = 0):
super().__init__(value, PermissionTypes)

Usage

# Create a user with two permissions

perm = UserPermission(PermissionTypes.ADMIN | PermissionTypes.USER)
perm.has(PermissionTypes.ADMIN) # → True
perm.has(PermissionTypes.GUEST) # → False

Using with Pydantic

Now let’s integrate it with Pydantic — and by extension, frameworks like FastAPI.

from pydantic import BaseModel

class User(BaseModel):
name: str
permissions: UserPermission
jane = User(name="Jane", permissions=PermissionTypes.ADMIN | PermissionTypes.USER)
assert jane.permissions.has(PermissionTypes.ADMIN)

All standard Pydantic features are supported, including serialization:

print(jane.model_dump_json(indent=2))

Which produces:

{
"name": "Jane",
"permissions": 3
}

Summary

  • Bitwise operations are fast and memory-efficient, but working with raw integers quickly becomes unreadable
  • BitAware combines the efficiency of bit flags with the readability and type safety of Python

If your application stores hundreds of thousands of records with simple toggles (feature flags, permissions, statuses), consider using BitAware before building yet another custom solution.

You’ll end up with less code to maintain — and fewer mistakes when dealing with bit-level logic.

--

--

Wojciech Nowicki
Wojciech Nowicki

Written by Wojciech Nowicki

By definition Software Architect ;)