In Python, operator precedence dictates the grouping and binding priority when multiple operators appear in an unparenthesized expression.
Precedence vs. Associativity: Precedence determines which operations bind to operands first (e.g. multiplication binds tighter than addition). Associativity determines evaluation direction (left-to-right for most arithmetic, but right-to-left for exponentiation **). To inspect binary operations, see bitwise operators. For object reference rules, see id().
The following table lists Python operators in order of decreasing precedence (highest at top, lowest at bottom):
| Operator | Description | Associativity |
|---|---|---|
( ), [ ], { } | Parentheses, indexing, slicing, literals | Left-to-right |
** | Exponentiation | Right-to-left |
+x, -x, ~x | Unary positive, negation, bitwise NOT | Right-to-left |
*, @, /, //, % | Multiplication, matrix multiply, division, floor div, modulus | Left-to-right |
+, - | Addition and subtraction | Left-to-right |
<<, >> | Bitwise left and right shifts | Left-to-right |
& | Bitwise AND | Left-to-right |
^ | Bitwise XOR | Left-to-right |
| | Bitwise OR | Left-to-right |
in, not in, is, is not, <, <=, >, >=, !=, == | Comparisons, identity, and membership tests | Chained |
not x | Boolean logical NOT | Right-to-left |
and | Boolean logical AND | Left-to-right (short-circuit) |
or | Boolean logical OR | Left-to-right (short-circuit) |
if - else | Conditional expression (ternary) | Right-to-left |
lambda | Lambda anonymous function expression | N/A |
:= | Walrus assignment expression | Right-to-left |
Python follows the algebraic convention PEMDAS (Parentheses, Exponents, Multiplication/Division, Addition/Subtraction). Multiplication and division share equal precedence and bind left-to-right:
# 1. Basic Arithmetic Grouping (Multiplication before Addition)
print("4 + 2 * 3 = ", 4 + 2 * 3) # 10: 2*3 evaluated first, then 4+6
print("4 + (2 * 3) = ", 4 + (2 * 3)) # 10: Explicit grouping
print("(4 + 2) * 3 = ", (4 + 2) * 3) # 18: Parentheses override natural precedenceTerminal Output:
4 + 2 * 3 = 10
4 + (2 * 3) = 10
(4 + 2) * 3 = 18
When two operators share the same precedence level, associativity governs evaluation direction:
10 / 2 * 6 is evaluated as (10 / 2) * 6 = 30.0.**): Evaluated right to left. E.g. 2 ** 3 ** 2 is evaluated as 2 ** (3 ** 2) = 2 ** 9 = 512, NOT (2 ** 3) ** 2 = 64.# 2. Left-to-Right vs. Right-to-Left Associativity
# Left-associative: evaluated from left to right
print("10 / 2 * 6 = ", 10 / 2 * 6) # (10 / 2) * 6 = 30.0
print("24 / 6 // 2 = ", 24 / 6 // 2) # (24 / 6) // 2 = 2.0
# Right-associative: Exponentiation (**) groups from RIGHT to LEFT!
print("2 ** 3 ** 2 = ", 2 ** 3 ** 2) # 2 ** (3 ** 2) = 2 ** 9 = 512
print("(2 ** 3) ** 2 =", (2 ** 3) ** 2) # (8) ** 2 = 64Terminal Output:
10 / 2 * 6 = 30.0
24 / 6 // 2 = 2.0
2 ** 3 ** 2 = 512
(2 ** 3) ** 2 = 64
The logical and operator has higher precedence than or. Omitting parentheses in multi-condition filters causes subtle logical defects:
# 3. Logical 'and' vs 'or' Precedence Trap
math_score = 40
english_score = 50
average = 45
# Requirement: Pass if average >= 50 AND (either math >= 40 or english >= 50)
# BUGGY (Unparenthesized): 'and' binds tighter than 'or'!
if math_score >= 40 or english_score >= 50 and average >= 50:
print("Unparenthesized result: Passed (BUG: math >= 40 satisfied left side)")
# FIXED: Explicit parentheses enforce intended business logic
if (math_score >= 40 or english_score >= 50) and average >= 50:
print("Correct evaluation: Passed")
else:
print("Correct evaluation: Failed")Terminal Output:
Unparenthesized result: Passed (BUG: math >= 40 satisfied left side)
Correct evaluation: Failed
In Python, relational comparison operators (==, !=, <, >) have higher precedence than bitwise operators (&, |, ^). This frequently catches developers transitioning from other languages:
# 4. Bitwise Operators vs. Comparison Precedence Trap
flags = 0b0100 # 4
# TRAP: '==' has HIGHER precedence than bitwise '&'!
# Evaluates as: flags & (1 == 0) -> flags & False -> 0 (Falsy!)
if flags & 1 == 0:
print("Trap result: Condition passed unexpectedly due to precedence")
# CORRECT: Use parentheses around bitwise operation
if (flags & 1) == 0:
print("Correct result: Flag bit 0 is not set")Terminal Output:
Trap result: Condition passed unexpectedly due to precedence
Correct result: Flag bit 0 is not set
In Python, chained comparisons like x < y < z are evaluated as (x < y) and (y < z) with automatic short-circuiting:
# 5. Chained Comparison Evaluation & Short-Circuiting
score = 85
# Chained comparison is syntactic sugar for: (70 <= score) and (score <= 100)
is_valid_grade = 70 <= score <= 100
print("Is 85 in [70, 100]:", is_valid_grade)
# Short-circuiting: If first comparison is False, second is never evaluated
def test_fn():
print("test_fn() was called!")
return 50
print("Chained short-circuit test:")
res = 100 < 50 < test_fn() # 100 < 50 is False, so test_fn() is NEVER called
print("Result:", res)Terminal Output:
Is 85 in [70, 100]: True
Chained short-circuit test:
Result: False
Constructing bulletproof permission filters using explicit parentheses to avoid precedence ambiguity:
# 6. Production Recipe: Secure Access Filter Validation
def check_access(is_superuser: bool, role: str, is_active: bool, is_banned: bool) -> bool:
"""Enforces strict security precedence:
User must NOT be banned, MUST be active, and either superuser or 'editor'."""
return not is_banned and is_active and (is_superuser or role == "editor")
print("Admin user access: ", check_access(False, "editor", True, False))
print("Banned admin access:", check_access(False, "editor", True, True))Terminal Output:
Admin user access: True
Banned admin access: False
Practice precedence calculations, right-associative power towers, and boolean expression trees in our interactive companion Jupyter Notebook:
Explore interactive code cells covering precedence levels, associativity traps, bitwise grouping, and self-validating unit assertions.
View on GitHub Run in Google ColabRun these unit checks directly in Python or your local environment to verify precedence rules:
# 7. Self-verifying unit test suite for Operator Precedence
# Test 1: Arithmetic precedence
assert 4 + 2 * 3 == 10
assert (4 + 2) * 3 == 18
# Test 2: Right-associative exponentiation
assert 2 ** 3 ** 2 == 512
assert (2 ** 3) ** 2 == 64
# Test 3: Logical 'and' before 'or'
assert (True or False and False) is True
assert ((True or False) and False) is False
# Test 4: Chained comparisons
assert (1 < 5 < 10) is True
assert (1 < 5 > 3) is True
# Test 5: Unary minus vs exponentiation
assert -2 ** 2 == -4 # -(2 ** 2) = -4
assert (-2) ** 2 == 4
print("All Operator Precedence unit assertions successfully verified!")Terminal Output:
All Operator Precedence unit assertions successfully verified!
Exponentiation ** has higher precedence than unary negation -. The expression is evaluated as -(2 ** 2) = -4. To negate before squaring, write (-2) ** 2 = 4.
and has higher precedence than or. In a or b and c, Python groups b and c first.
Associativity determines evaluation order for operators of equal precedence. Most operators are left-to-right, while exponentiation ** and assignment = are right-to-left.
In production code, explicit parentheses are strongly recommended whenever combining mixed operators (e.g., bitwise with comparisons or and with or) to guarantee readability and avoid bugs.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.