Operator Precedence and Associativity

Precedence decides which operator runs first and associativity decides the direction when two have equal rank. Both are worth knowing, and brackets are worth using anyway.

Why precedence exists

An expression such as 2 + 3 * 4 has two possible readings. Precedence removes the ambiguity by ranking the operators: multiplication binds more tightly than addition, so the answer is 14, not 20.

print(2 + 3 * 4)        # 14
print((2 + 3) * 4)      # 20

The table, highest binding first

LevelOperatorsAssociativity
1() [] {} - grouping and display-
2x[i] x(...) x.attr - subscript, call, attributeLeft
3**Right
4+x -x ~x - unaryRight
5* / // %Left
6+ -Left
7<< >>Left
8&Left
9^Left
10|Left
11== != < > <= >= is is not in not inChained
12notRight
13andLeft
14orLeft
15if ... else - conditional expressionRight
16lambda-
17:=Right

Do not memorise this. Memorise four facts and bracket the rest:

  1. ** binds tighter than unary minus and associates right.
  2. Arithmetic binds tighter than comparison.
  3. Comparison binds tighter than not, and and or.
  4. Bitwise operators sit between arithmetic and comparison, which is where most surprises come from.

Associativity

When two operators have equal precedence, associativity decides the grouping.

print(100 - 30 - 20)         # 50, left to right: (100 - 30) - 20
print(2 ** 3 ** 2)           # 512, right to left: 2 ** (3 ** 2)
print((2 ** 3) ** 2)         # 64

Exponentiation is the one arithmetic operator that associates right, matching mathematical convention.

Unary minus and **

print(-2 ** 2)         # -4,  parsed as -(2 ** 2)
print((-2) ** 2)       # 4
print(2 ** -1)         # 0.5, the minus on the right is fine

Worked examples

print(10 + 20 * 3 ** 2 // 4 - 5)
3 ** 2        -> 9          (** first)
20 * 9        -> 180        (* and // left to right)
180 // 4      -> 45
10 + 45       -> 55         (+ and - left to right)
55 - 5        -> 50
print(True or False and False)
and binds tighter than or:
True or (False and False)
True or False
True

Reading that as (True or False) and False would give False. This is the most common logical precedence mistake.

x = 5
print(not x == 5)        # False. Parsed as not (x == 5).
print((not x) == 5)      # False, but for a completely different reason.

The bitwise trap

x = 6
print(x & 1 == 0)        # False - surprising
print((x & 1) == 0)      # True  - what was meant

== binds tighter than &, so the first line computes x & (1 == 0), which is 6 & False, which is 0, which is falsy. Always bracket bitwise operations that appear next to a comparison.

Chained comparisons in the ordering

print(1 < 2 < 3)              # True
print(1 < 2 < 3 == 3)         # True  - all four terms chain
print(3 > 2 == 2)             # True  - (3 > 2) and (2 == 2)

All comparison operators share one precedence level and chain rather than nest. a < b < c is never (a < b) < c.

The conditional expression

age = 20
label = "adult" if age >= 18 else "minor"
print(label)                   # adult

# It binds very loosely, so this may not be what you expect:
print("total: " + "high" if 5 > 3 else "low")     # high
print("total: " + ("high" if 5 > 3 else "low"))   # total: high

The conditional expression sits almost at the bottom of the table, so the concatenation on its left is absorbed into the if branch. Bracket it.

Common mistakes

  • Assuming -2 ** 2 is 4.
  • Assuming 2 ** 3 ** 2 is 64.
  • Writing x & 1 == 0 without brackets.
  • Reading a or b and c left to right instead of noticing that and binds first.
  • Mixing a conditional expression into a larger expression without brackets.
  • Writing not a == b when a != b was meant.

Best practices

  • Bracket for the reader, not for the parser. If a colleague would have to check the table, add the brackets.
  • Split a long expression across intermediate variables with meaningful names; it is easier to read and easier to debug.
  • Always bracket bitwise operators inside comparisons.
  • Keep conditional expressions short and standalone.

Practice

  1. Evaluate by hand, then check: 2 + 3 * 4 ** 2 // 8 - 1.
  2. Evaluate by hand, then check: not True and False or True.
  3. Explain why -3 ** 2 is -9 and how to make it 9.
  4. Fix if flags & 4 == 4: so it tests the intended bit.
  5. Rewrite result = a if a > b else b if b > c else c with brackets showing how Python groups it.

Conclusion

Four rules cover almost everything: ** is right associative and beats unary minus, arithmetic beats comparison, comparison beats the logical words, and bitwise operators hide awkwardly in between. When in doubt, add brackets; they cost nothing and they never lie.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Python notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.