2026, Jan 14 07:00
Avoiding Bugs from Mixing Bitwise | and Logical or in Python Comparisons: Understanding Precedence
Learn why Python's operator precedence makes x == z | x == y misbehave. See how | differs from or, how chained comparisons work, and how to write clear code.
Unexpected truth values from boolean-looking expressions in Python often trace back to operator precedence. A classic example is mixing bitwise and comparison operators and getting a result that looks wrong at first glance, but is exactly what the language specifies.
Reproducing the behavior
Consider the following snippet. The first print produces False, the second prints True.
x = 7
y = 8
z = 7
print(x == z | x == y)
print((x == z) | (x == y))What’s actually happening
The bitwise OR operator, |, has higher precedence than the equality operator, ==. That means the expression without parentheses is not grouped the way many people expect. Instead of checking whether either comparison is true, Python first evaluates the bitwise OR between z and x, and then performs a chained comparison.
Formally, the expression
x == z | x == yis parsed as
x == (z | x) == yChained comparisons in Python are equivalent to combining the individual comparisons with logical and, so the previous line behaves like
x == (z | x) and (z | x) == yIn this specific setup, the first comparison is True and the second is False, therefore the whole expression evaluates to False. By contrast, the second print adds parentheses, turning each side of | into a boolean before the bitwise operator runs, which yields True for this data.
What to use instead
If the intent is a logical disjunction, use the logical operator or. It has lower precedence than comparisons, so you don’t need extra parentheses to make the grouping explicit:
x == z or x == yThis is equivalent to
(x == z) or (x == y)and expresses the intent directly.
Why this trips people up
The confusion stems from the visual similarity between bitwise and logical operators and from the fact that comparisons chain. Bitwise operators bind tighter than ==, while or binds looser than comparisons. As a result, bitwise usage without parentheses easily turns a seemingly boolean expression into a chained comparison with a different truth table.
Deeper notes on booleans and bitwise operations
There are a few subtleties worth knowing when mixing booleans and bitwise operators. In Python, bool overrides some inherited methods (for example, __or__) but not others (for example, __neg__). There appears to be a special case when both operands are bool for the operators |, &, and ^, and in practice the bitwise OR between boolean operands keeps the bool type. Negation flows to int semantics: -True is -1, which is not a boolean. For bitwise operations, False equals 0 and True equals 1, which explains why bitwise combinations of booleans behave numerically unless you rely on the dedicated boolean behavior.
Why you should care
Clarity around precedence prevents subtle bugs and makes code easier to read. Using or for logical intent avoids the need for parentheses and keeps expressions aligned with how readers interpret them. Resorting to bitwise operators to combine truth values is unusual and, because of their higher precedence, forces you to parenthesize aggressively.
Takeaways
When you want a logical OR between comparisons, write it with or. If you do need bitwise operators for numeric operands, add parentheses explicitly. And remember that chained comparisons like a == b == c are a distinct construct; mixing them with | without parentheses changes both meaning and result.