PythonOperator Precedence

Operator Precedence

When an expression mixes several operators — 2 + 3 * 4 — Python has to decide which operation happens first. That decision is governed by operator precedence: a fixed ranking of which operators "bind tighter" than others. Operators with higher precedence are evaluated before operators with lower precedence, regardless of the order they appear in the line. Get the ranking wrong in your head, and you get a number that looks plausible but is silently incorrect — one of the most common sources of subtle bugs in otherwise working code.

The full precedence table

The table below lists Python's operators from highest precedence (evaluated first) to lowest (evaluated last). Operators on the same row share equal precedence and are then resolved by associativity — the order in which same-precedence operators are applied when several appear side by side.

Precedence

Operator(s)

Description

Associativity

1 (highest)

()

Parentheses — grouping, forces evaluation order

n/a

2

**

Exponentiation

Right-to-left

3

+x, -x, ~x

Unary plus, unary minus, bitwise NOT

Right-to-left

4

*, /, //, %

Multiplication, division, floor division, modulo

Left-to-right

5

+, -

Addition, subtraction

Left-to-right

6

<<, >>

Bitwise left shift, right shift

Left-to-right

7

&

Bitwise AND

Left-to-right

8

^

Bitwise XOR

Left-to-right

9

|

Bitwise OR

Left-to-right

10

==, !=, >, <, >=, <=, is, is not, in, not in

Comparisons, identity, and membership tests

Left-to-right (chained)

11

not

Boolean negation

Right-to-left

12

and

Boolean AND

Left-to-right

13 (lowest)

or

Boolean OR

Left-to-right

Why exponentiation is right-associative

Most binary operators in Python are left-associative: when the same operator appears twice in a row, evaluation proceeds left to right. ** is the notable exception — it is right-associative, which matches how exponent towers are conventionally read in mathematics.

** chains right-to-left

Python
print(2 ** 3 ** 2)
# Evaluated as 2 ** (3 ** 2) = 2 ** 9 = 512
# NOT (2 ** 3) ** 2 = 8 ** 2 = 64

print(-2 ** 2)
# Unary minus has LOWER precedence than **, so this is -(2 ** 2) = -4
# NOT (-2) ** 2 = 4
Unary minus and ** together are easy to misread
`-2 ** 2` is `-4`, not `4`, because `**` binds tighter than unary `-`. If you actually want to square a negative number, you must parenthesize it explicitly: `(-2) ** 2`.
Walking through a mixed expression step by step

Here is a single expression that touches five different precedence levels at once. Rather than evaluating left to right, Python resolves it strictly by precedence: exponentiation first, then the multiplicative operators (*, //, %) left to right, then the additive operators (+, -) left to right.

A mixed expression

Python
result = 2 + 3 * 4 ** 2 // 5 - 1
print(result)
10

Step by step, following the precedence table:

  1. 4 ** 2 — exponentiation runs first (highest precedence among the operators present): 4 ** 2 = 16. The expression is now 2 + 3 * 16 // 5 - 1.
  2. 3 * 16 — multiplication is next, evaluated left to right among the multiplicative operators: 3 * 16 = 48. Now 2 + 48 // 5 - 1.
  3. 48 // 5 — floor division shares precedence with *, and since it appears next moving left to right, it runs now: 48 // 5 = 9 (floor division truncates toward negative infinity, and 48 / 5 = 9.6, so the result is 9). Now 2 + 9 - 1.
  4. 2 + 9 — addition and subtraction share the lowest precedence of the operators involved and are evaluated left to right: 2 + 9 = 11. Now 11 - 1.
  5. 11 - 1 — final subtraction: 11 - 1 = 10.

The result is 10. Notice that at no point did evaluation simply proceed left to right across the whole expression — the exponent and the multiplicative operators cut in first even though they are not first in reading order.

Comparisons and boolean operators sit at the bottom

Comparisons (==, <, in, is, and friends) all bind tighter than not, and, and or. This is why you can write conditions without extra parentheses and have them mean what they look like they mean.

Comparisons resolve before boolean logic

Python
x = 5
print(x > 2 and x < 10)
# Equivalent to (x > 2) and (x < 10) -> True and True -> True

print(not x == 5)
# Equivalent to not (x == 5) -> not True -> False

print(1 < 2 < 3)
# Chained comparison: equivalent to (1 < 2) and (2 < 3) -> True
Chained comparisons are not what they look like
`1 < 2 < 3` is not evaluated as `(1 < 2) < 3`. Python treats chains of comparison operators specially: `a < b < c` means `(a < b) and (b < c)`, with each operand evaluated only once. This is convenient for range checks like `0 <= index < len(items)`, but it also means `1 < 2 > 0` is valid and evaluates to `True`.
Bitwise operators are lower precedence than comparisons

A very common bug: bitwise &, |, and ^ bind more loosely than comparisons, which is the opposite of what many people assume coming from C-like intuition about "and means &". This means a == 1 & b == 2 does not do what it looks like it does.

Bitwise vs comparison precedence trap

Python
a, b = 1, 2

# Looks like it should check "a == 1 AND b == 2", but it doesn't:
print(a == 1 & b == 2)
# & binds tighter than ==, so this is actually a == (1 & b) == 2
# 1 & 2 is 0, so this becomes a == 0 == 2 -> False (chained comparison)

# What you almost certainly meant:
print((a == 1) & (b == 2))
# Explicit parentheses -> True & True -> True (or 1, depending on types)
Tip
Precedence bugs are notoriously hard to catch in code review because the buggy expression still looks reasonable at a glance — the eye reads left to right, but Python does not. Use parentheses generously around mixed arithmetic, bitwise, and comparison operators even when the language does not require them. The extra characters cost nothing at runtime and make the intended evaluation order obvious to the next reader, including future you.