A deep dive into Python’s bitwise magic methods like __and__, __or__, and __xor__. Great for advanced learners who want to write smarter, cleaner code with custom objects and operators.
You’ve probably used operators like &
, |
, ^
, or ~
in Python before. They're called bitwise operators because they work at the level of binary digits, or bits. So instead of working with whole numbers or booleans, they deal with the 0s and 1s that make up numbers underneath. But have you ever wondered how Python knows what to do when you write a & b
? Or how you can make your own class respond to those operators?
That’s where bitwise special methods come in, things like __and__
, __or__
, __xor__
, etc. They let you control how bitwise operators work on custom objects, just like how __add__
lets you control +
.
Let’s break it all down in simple terms, with code and practical ideas you can actually use.
Bitwise special methods are part of Python's “dunder methods”. These are the methods you can define in a class to control how operators work.
Here’s a quick list of the main ones related to bitwise operations:
Operator | Special Method | Example Syntax |
---|---|---|
& (AND) |
__and__(self, other) |
a & b |
| (OR) |
__or__(self, other) |
a | b |
^ (XOR) |
__xor__(self, other) |
a ^ b |
~ (NOT / invert) |
__invert__(self) |
~a |
<< (left shift) |
__lshift__(self, other) |
a << 1 |
>> (right shift) |
__rshift__(self, other) |
a >> 1 |
Also, Python provides reflected versions for operations where the left operand doesn’t handle the logic:
Method | Used when |
---|---|
__rand__ |
If other.__and__(self) is used |
__ror__ |
If other.__or__(self) is used |
__rxor__ |
Same idea for XOR |
You might wonder why would I ever override these? Here are a few real-world cases:
Let’s build a simple class to manage user permissions using bit flags.
class Permissions:
def __init__(self, value=0):
self.value = value # store as an integer (bit field)
def __and__(self, other):
return Permissions(self.value & other.value)
def __or__(self, other):
return Permissions(self.value | other.value)
def __xor__(self, other):
return Permissions(self.value ^ other.value)
def __invert__(self):
return Permissions(~self.value)
def __str__(self):
return f"Permissions({bin(self.value)})"
READ = Permissions(0b001)
WRITE = Permissions(0b010)
EXECUTE = Permissions(0b100)
user_perm = READ | WRITE
print(user_perm) # Permissions(0b11)
admin_perm = user_perm | EXECUTE
print(admin_perm) # Permissions(0b111)
read_only = admin_perm & READ
print(read_only) # Permissions(0b1)
no_exec = admin_perm ^ EXECUTE
print(no_exec) # Permissions(0b11)
We’re using bitwise operations to combine permissions (OR), check for overlapping permissions (AND), or toggle one (XOR). This is super common in systems like file permissions, user roles, or feature flags.
__rand__
and Fallback LogicSometimes, the left-hand object doesn’t know how to handle the operation, especially if it’s a built-in type like int
. In that case, Python tries the right-hand operand’s reflected method.
class Flag:
def __rand__(self, other):
return f"Reflected AND with {other}"
print(5 & Flag()) # Calls Flag().__rand__(5)
Here, Python first tries 5.__and__(Flag())
, which returns NotImplemented
, so it then tries Flag().__rand__(5)
. That’s why this fallback system exists.
__lshift__
and __rshift__
Sometimes you want to move bits left or right, like multiplying/dividing by powers of 2.
class BitShifter:
def __init__(self, value):
self.value = value
def __lshift__(self, other):
return BitShifter(self.value << other)
def __rshift__(self, other):
return BitShifter(self.value >> other)
def __str__(self):
return bin(self.value)
shifter = BitShifter(0b0010)
print(shifter << 1) # 0b100
print(shifter >> 1) # 0b1
Great for things like image processing, encoding/decoding, or low-level optimization.
If your __and__
method gets something unexpected like an int
, handle it carefully or return NotImplemented
.
def __and__(self, other):
if not isinstance(other, Permissions):
return NotImplemented
return Permissions(self.value & other.value)
and
, or
, not
→ used for boolean logic&
, |
, ~
→ used for binary/bitwise operationsThey’re completely different things!
~
uses two’s complementThat means ~0b0001
is not 0b1110
, but actually -0b0010
. It flips all bits, including the sign bit.
Bitwise special methods like __and__
, __or__
, and __xor__
give you low-level control over how your custom classes behave with binary operators.
They're great for:
By implementing these dunder methods, your objects can talk to Python’s operators directly and that gives you both power and elegance.