027. Operator Precedence
Explicit parentheses prevent ambiguity
027. Operator Precedence
RAM Manager — Memory Formulas
Aryan’s RAM manager computes several metrics from raw numbers. He transcribes the formulas directly from the spec sheet — but forgets to add parentheses where needed, and Python’s default operator precedence gives the wrong answer.
- available_ram: total RAM minus (OS overhead + reserved). Written as
total - overhead + reserved, which subtracts overhead but then adds reserved instead of subtracting it. - usage_after_discount:
used * 1 - discount_mbshould beused * (1 - discount_mb / total)— without parentheses,*binds first. - effective_bandwidth: a weighted average formula where the denominator must be grouped.
The lesson: Python follows PEMDAS like C, but missing parentheses are silent bugs. The code runs, produces a number, and looks plausible — it is just wrong.
💡 Fun fact: The classic average bug a + b + c / 3 (dividing only c instead of the whole sum) has caused real-world disasters — a similar precedence error in a NASA Mars Climate Orbiter mission contributed to a $327 million spacecraft being lost in 1999, because unit conversions were applied at the wrong point in a formula.
⚠️ Watch out: The most frequent precedence mistake is writing an average without parentheses: a + b + c / 3 divides only c by 3. Always write (a + b + c) / 3. When in doubt, add parentheses — they cost nothing and make your intent explicit to every reader.
🤔 Think about it: Python’s ** operator is right-associative, meaning 2 ** 3 ** 2 = 2 ** 9 = 512, not 8 ** 2 = 64. Most other operators are left-associative. Why might the designers have chosen right-associativity specifically for exponentiation?
Learning objectives
- Know the basic Python operator precedence order
- Use parentheses to explicitly control evaluation order
- Identify common precedence mistakes (average, ring area, discounts)
Key concepts
- operator precedence
- parentheses
- PEMDAS
Try it
Concept detail
Python operator precedence (highest first): ** > unary (+x, -x, ~x) > * / // % > + - > comparisons > not > and > or. When in doubt: use parentheses — they cost nothing and make intent explicit. 2 + 3 * 4 = 14 (not 20): multiplication binds before addition. a + b + c / 3 divides only c, not the sum — a classic average bug. 2 ** 3 ** 2 = 512: ** is right-associative, so 2**(32) = 29. -(2**2) = -4 (not 4): ** binds tighter than unary minus.
Solution
import math
def area_of_ring(outer_r, inner_r):
return math.pi * (outer_r ** 2 - inner_r ** 2)
def discount_price(price, discount_pct):
return price * (1 - discount_pct / 100)
def average_of_three(a, b, c):
return (a + b + c) / 3Tests
import math
def test_area_of_ring():
# outer=5, inner=3 → π*(25-9) = π*16 ≈ 50.27
result = area_of_ring(5, 3)
assert abs(result - math.pi * 16) < 0.001
def test_discount_price():
# 100 * (1 - 20/100) = 100 * 0.8 = 80
result = discount_price(100, 20)
assert abs(result - 80.0) < 0.001
def test_average_equal():
# (3 + 3 + 3) / 3 = 3.0
result = average_of_three(3, 3, 3)
assert abs(result - 3.0) < 0.001
def test_average_mixed():
# (1 + 2 + 3) / 3 = 2.0
result = average_of_three(1, 2, 3)
assert abs(result - 2.0) < 0.001