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 == y

is parsed as

x == (z | x) == y

Chained 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) == y

In 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 == y

This 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.