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.
- Basics
- Data Types
- Operators
- Strings
- Control Flow
- Lists
- Tuples
- Sets
- Dictionaries
- Comprehensions
- Functions
- Advanced Functions
- Recursion
- Exception Handling
- File Handling
- Modules
- Standard Library
- OOP
- Advanced OOP
- Iterators and Generators
- Decorators
- Context Managers
- Descriptors and Dataclasses
- Python Internals
- Concurrency
- Regular Expressions
- Serialization
- Command Line Python
- Testing and Debugging
- Type Hints
- Performance
- Python Security
- DSA with Python
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) # 20The table, highest binding first
| Level | Operators | Associativity |
|---|---|---|
| 1 | () [] {} - grouping and display | - |
| 2 | x[i] x(...) x.attr - subscript, call, attribute | Left |
| 3 | ** | Right |
| 4 | +x -x ~x - unary | Right |
| 5 | * / // % | Left |
| 6 | + - | Left |
| 7 | << >> | Left |
| 8 | & | Left |
| 9 | ^ | Left |
| 10 | | | Left |
| 11 | == != < > <= >= is is not in not in | Chained |
| 12 | not | Right |
| 13 | and | Left |
| 14 | or | Left |
| 15 | if ... else - conditional expression | Right |
| 16 | lambda | - |
| 17 | := | Right |
Do not memorise this. Memorise four facts and bracket the rest:
**binds tighter than unary minus and associates right.- Arithmetic binds tighter than comparison.
- Comparison binds tighter than
not,andandor. - 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) # 64Exponentiation 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 fineWorked 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 -> 50print(True or False and False)and binds tighter than or:
True or (False and False)
True or False
TrueReading 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: highThe 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 ** 2is4. - Assuming
2 ** 3 ** 2is64. - Writing
x & 1 == 0without brackets. - Reading
a or b and cleft to right instead of noticing thatandbinds first. - Mixing a conditional expression into a larger expression without brackets.
- Writing
not a == bwhena != bwas 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
- Evaluate by hand, then check:
2 + 3 * 4 ** 2 // 8 - 1. - Evaluate by hand, then check:
not True and False or True. - Explain why
-3 ** 2is-9and how to make it9. - Fix
if flags & 4 == 4:so it tests the intended bit. - Rewrite
result = a if a > b else b if b > c else cwith 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.