PYTHON / OPERATORS
Operator precedence and associativity
Predict how Python groups any unparenthesized expression by applying precedence first, then associativity, and confirm the reading with parentheses.
What you will learn
- Rank operators by precedence to find which one grabs a shared operand
- Apply left-to-right associativity, and right-to-left for **
- Explain why -2 ** 2 is -4 but 2 ** -1 is 0.5
- Spot places where Python's precedence differs from C-style expectations
Understanding Operator precedence and associativity
Python does not read an expression left to right; it builds a tree. When two operators compete for the same operand, the one with higher precedence wins that operand. In 1 << 2 + 3, both << and + want the 2, and + binds tighter, so the parse is 1 << (2 + 3), giving 32 rather than 4 + 3. The precedence order from loosest to tightest is: the conditional expression x if c else y, then or, and, not x, then all comparisons together with in and is, then |, then ^, then &, then << and >>, then + and -, then * / // % @, then unary +x -x ~x, then **, and finally calls, subscripts and attribute access.
Associativity only matters as a tie-break between operators of equal precedence. Nearly everything in Python is left-associative, so 10 - 3 - 2 is (10 - 3) - 2 and 2 * 3 % 4 is (2 * 3) % 4. The exception is **, which is right-associative: 2 ** 3 ** 2 is 2 ** (3 ** 2), or 512, matching how exponent towers are read in mathematics. The conditional expression is also right-associative, which is why a if p else b if q else c chains as a if p else (b if q else c).
The asymmetry around ** and unary minus catches almost everyone. Unary minus has lower precedence than **, so on the left of the operator the exponent binds first: -2 ** 2 is -(2 ** 2), which is -4. On the right side there is nothing for ** to compete with, so the grammar allows a unary operator directly in the exponent: 2 ** -1 is 2 ** (-1), which is 0.5. Precedence decides grouping only; it never changes the fact that operands are themselves evaluated left to right.
print(2 ** 3 ** 2) # right-associative: 2 ** (3 ** 2)
print(-2 ** 2) # ** tighter than unary minus: -(2 ** 2)
print(2 ** -1) # unary minus allowed in the exponent
print(10 - 3 - 2) # left-associative: (10 - 3) - 2
print(1 << 2 + 3) # + tighter than <<: 1 << 5
print(not 1 == 2) # comparison tighter than not: not (1 == 2)Precedence decides which operator claims a contested operand, and associativity breaks the tie when the competing operators have equal precedence.
Worked examples
and beats or, and the conditional loses to everything
Shows that and binds tighter than or, and that x if c else y grabs the widest possible operands.
a, b, c = True, False, False
print(a or b and c)
print((a or b) and c)
print(1 + 1 if b else 9)
print(1 + (1 if b else 9))Example explained
Line 1a or b and c groups as a or (b and c), so the True on the left decides the result.
Line 2Forcing (a or b) and c changes the tree and yields False, proving the grouping was not left to right.
Line 31 + 1 if b else 9 is (1 + 1) if b else 9: the conditional has the lowest precedence, so it swallows the whole 1 + 1 as its true-branch.
Line 4Parentheses around the conditional put it inside the addition instead, giving 1 + 9.
Bitwise operators bind tighter than comparisons
Demonstrates that & outranks ==, which turns a mistyped compound test into a chained comparison.
x, y = 0, 1
print(x == 0 & y == 1)
print((x == 0) & (y == 1))
print(0 & y)Example explained
Line 1& is tighter than ==, so the first line parses as x == (0 & y) == 1, a three-way chained comparison.
Line 20 & y is 0, so the chain becomes 0 == 0 == 1, which is False.
Line 3The parenthesized version compares first and combines the two booleans, giving the intended True.
Line 4In C the ranking is reversed, which is why code translated from C-family languages breaks here.
Important notes
Precedence controls grouping, not the order in which operands are evaluated: in f() + g() * h(), f() still runs first even though the multiplication groups first.
Chained comparisons such as a < b < c come from a separate grammar rule, not from precedence, so you cannot explain them by ranking < against itself.
Common mistakes
Reading -3 ** 2 as (-3) ** 2 and expecting 9; Python gives -9, which silently corrupts formulas like squared deviations where the base can be negative.
Assuming ** is left-associative like the other arithmetic operators, so 2 ** 3 ** 2 is expected to be 64 when it is 512.
Writing a == 1 & b == 2 as a compound test; because & outranks ==, it becomes the chained comparison a == (1 & b) == 2 and returns a bool that has nothing to do with either condition.
Try it yourself
Change, predict, then run
Write down your prediction for each of 2 ** 2 ** 3, -2 ** 0.5, 4 - 1 - 1 - 1, 3 | 1 == 1 and False == False in [False], then print each one and print a fully parenthesized version that produces the same value.
Open the Python workspaceCheck your understanding
What does -2 ** 2 ** 3 evaluate to, and why?
- -256, because ** groups right to left and unary minus applies last
- 256, because the negative base is squared before the outer exponent
- -64, because the exponents group left to right and the minus applies last
- 64, because (-2) ** 2 is 4 and 4 ** 3 is 64
Show answer
** is right-associative, so 2 ** 3 is 8, then 2 ** 8 is 256; unary minus has lower precedence than ** so it negates the whole power, giving -256. Option 4 is tempting because it reads left to right and attaches the minus to the base, but Python never binds unary minus to the left operand of **.