Bitwise Operators in Python
Six operators that work on the individual bits of an integer. Rarely needed in everyday code, routinely needed for flags, masks, permissions and interview questions.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
What bitwise means
Every integer is stored as a pattern of bits. Bitwise operators ignore the numeric value and work on those bits one position at a time.
print(bin(12)) # 0b1100
print(bin(10)) # 0b1010| Operator | Name | Bit rule | 12 and 10 |
|---|---|---|---|
& | AND | 1 only if both bits are 1 | 8 |
| | OR | 1 if either bit is 1 | 14 |
^ | XOR | 1 if the bits differ | 6 |
~ | NOT | Flips every bit | ~12 is -13 |
<< | Left shift | Moves bits left, filling with 0 | 12 << 1 is 24 |
>> | Right shift | Moves bits right, discarding | 12 >> 1 is 6 |
Working through the arithmetic
12 = 1 1 0 0
10 = 1 0 1 0
-------
& 1 0 0 0 = 8
| 1 1 1 0 = 14
^ 0 1 1 0 = 6print(12 & 10) # 8
print(12 | 10) # 14
print(12 ^ 10) # 6
print(~12) # -13
print(12 << 1) # 24
print(12 >> 1) # 6Why ~12 is -13
Python integers are conceptually signed and of unlimited width, and negative numbers use two's complement. Flipping every bit of n always yields -(n + 1). That single identity is easier to remember than the bit pattern.
for n in [0, 1, 5, 12]:
print(n, "->", ~n) # 0 -> -1, 1 -> -2, 5 -> -6, 12 -> -13Shifting is multiplying and dividing by powers of two
print(5 << 1) # 10 same as 5 * 2
print(5 << 3) # 40 same as 5 * 2 ** 3
print(40 >> 3) # 5 same as 40 // 2 ** 3
print(-7 >> 1) # -4 floors, exactly like //Do not use shifting as a speed trick in Python. The interpreter overhead dominates, son * 2is not measurably slower thann << 1, and it is far clearer. Shift when you mean bits, multiply when you mean arithmetic.
The real use: flags and masks
Bitwise operators earn their place when several yes or no settings are packed into one integer. File permissions, hardware registers and network protocols all work this way.
READ = 0b0001 # 1
WRITE = 0b0010 # 2
EXECUTE = 0b0100 # 4
DELETE = 0b1000 # 8
# Grant several permissions at once.
permissions = READ | WRITE
print(bin(permissions)) # 0b11
# Test a permission.
print(bool(permissions & READ)) # True
print(bool(permissions & EXECUTE)) # False
# Add one.
permissions |= EXECUTE
print(bool(permissions & EXECUTE)) # True
# Remove one.
permissions &= ~WRITE
print(bool(permissions & WRITE)) # False
# Toggle one.
permissions ^= DELETE
print(bool(permissions & DELETE)) # TrueThose four lines - set with |, test with &, clear with &= ~, toggle with ^ - are the entire vocabulary of flag handling.
Classic bit tricks
# Is n even?
n = 14
print(n & 1 == 0) # True; the last bit is the parity
# Is n a power of two?
def is_power_of_two(n):
return n > 0 and n & (n - 1) == 0
print(is_power_of_two(16)) # True
print(is_power_of_two(18)) # False
# Count the set bits.
print(bin(29).count("1")) # 4
print((29).bit_count()) # 4, Python 3.10 and later
# Swap two numbers without a temporary (a curiosity, not a recommendation).
a, b = 5, 9
a ^= b
b ^= a
a ^= b
print(a, b) # 9 5n & (n - 1) clears the lowest set bit. If that leaves zero, there was exactly one set bit, so n was a power of two. This appears in interviews constantly.
Useful integer methods
print((255).bit_length()) # 8, bits needed to represent it
print((255).to_bytes(2, "big")) # b'\x00\xff'
print(int.from_bytes(b"\x01\x00", "big")) # 256Bitwise operators on sets
The same symbols are reused by set, where they mean intersection, union, difference and symmetric difference. That is covered fully in the sets note; it is mentioned here so the shared symbols are not a surprise.
a = {1, 2, 3}
b = {2, 3, 4}
print(a & b) # {2, 3}
print(a | b) # {1, 2, 3, 4}
print(a ^ b) # {1, 4}Common mistakes
- Confusing
&withand.5 & 3is1;5 and 3is3. They are unrelated. - Expecting
~5to be-5. It is-6. - Using bitwise operators on floats. It is a
TypeError; bits are defined for integers only. - Forgetting that
&binds more loosely than==, sox & 1 == 0parses asx & (1 == 0). Bracket it:(x & 1) == 0. - Using shifts for arithmetic in the belief that it is faster.
Best practices
- Name your flags as constants in capitals and define them as powers of two.
- Bracket bitwise expressions generously; the precedence rules rarely match intuition.
- Use
bin()while learning and debugging so you can see what happened. - Consider
enum.Flagfrom the standard library for real permission systems; it gives the same behaviour with readable names.
Practice
- Compute
25 & 19,25 | 19and25 ^ 19by writing out the bits, then check with Python. - Explain why
~n == -(n + 1)holds for every integer. - Define four permission flags and write functions to grant, revoke, toggle and test one.
- Write a function that returns the number of 1 bits in an integer without using
binorbit_count. - Explain why
x & 1 == 0does not do what it looks like, and fix it.
Conclusion
Bitwise operators are a small, self contained corner of Python. You will not need them often, but when you meet packed flags, protocol headers or an interview question about powers of two, nothing else will do the job as directly.