Topic 1.7
Arithmetic, Precedence and Augmented Assignment
In one line
Python follows a fixed order of operations (precedence): powers first, then multiplication and division, then addition and subtraction. Use parentheses to be clear, use += and friends to update a variable, and reach for the math module and built-ins like abs, min and max for more.
Think of it like this
Reading a shopping bill. "2 packets at 10 rupees plus 5 rupees delivery" means 2 x 10 + 5 = 25, because multiplication happens before addition. Everyone agrees on the order, so no one asks. Python has the same agreement, called operator precedence: some operators go first, regardless of how you read left to right.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Operator
- A symbol that does an operation, like
+or**. - Operand
- A value an operator works on. In
3 + 4, the 3 and the 4. - Precedence
- The order in which operators run when a line has several.
- Associativity
- Which way operators of the same level group: left to right, or (for
**) right to left. - Augmented assignment
- An update such as
x += 1, short forx = x + 1. - Unary minus
- A minus sign in front of one value, like
-x. - math module
- A standard-library module of maths functions such as
sqrtandfloor. - Built-in function
- A function available everywhere without import, such as
abs,min,max.
Step by step
01Multiplication before addition
Python's calculator rules match school maths. 2 + 3 * 4 is 2 + 12, which is 14. To add first, use parentheses.
print(2 + 3 * 4) # 14
print((2 + 3) * 4) # 2002The power surprises
Read -3 ** 2 as "minus (three squared)": the power runs first, and the minus is applied to the result. That gives -9. With parentheses, (-3) ** 2 is 9.
For 2 ** 3 ** 2 Python works from the right: 3 ** 2 is 9, then 2 ** 9 is 512. This matches maths, where a tower of powers is read from the top down.
print(-3 ** 2, (-3) ** 2) # -9 9
print(2 ** 3 ** 2, (2 ** 3) ** 2) # 512 6403Same level goes left to right
10 - 4 - 3 groups as (10 - 4) - 3. Likewise 100 / 10 / 5 is (100 / 10) / 5, which is 2.0. Only ** groups the other way.
print(10 - 4 - 3, 100 / 10 / 5) # 3 2.0
print(7 // 2 * 2 + 7 % 2) # 704Update a variable: +=
A score that grows is the most common use of +=. Each augmented assignment reads the old value, works it out, and rebinds the name. After score /= 4 the type changed from int to float, because / always gives a float.
score = 10
score += 5 # 15
score -= 3 # 12
score *= 2 # 24
score /= 4 # 6.0
score **= 2 # 36.0
print(score)05Built-ins and the math module
import math makes the module available, and you write math.sqrt(16). Constants such as math.pi have no brackets because they are values, not functions.
math.sqrt(2) and 2 ** 0.5 give the same float. Use math.sqrt for clarity and ** for general roots.
In the last line, math.gcd(12, 18) is the biggest whole number that divides both (6). math.factorial(5) is 5 x 4 x 3 x 2 x 1 (120). math.hypot(3, 4) is the length of the long side of a right-angled triangle whose short sides are 3 and 4 (5.0).
import math
print(math.sqrt(16), math.pi)
print(abs(-7), max(3, 9, 5), min(3, 9, 5))
print(math.gcd(12, 18), math.factorial(5), math.hypot(3, 4))06Four ways to remove a fraction
For -2.5: math.floor gives -3 (down), math.ceil gives -2 (up), math.trunc gives -2 (towards zero), round gives -2 (nearest even). Know which one you mean: they only agree on whole numbers.
import math
print(math.floor(-2.5), math.ceil(-2.5), math.trunc(-2.5), round(-2.5))
# -3 -2 -2 -2Try it yourself
- 1
Predict, then run
Predict
8 - 2 * 3 ** 2. Work it out on paper: power, then multiply, then subtract. Then run it. - 2
Average of three marks
With
a = 70,b = 85,c = 90, compute the average. First write it wrongly asa + b + c / 3and see the result, then add the parentheses it needs. - 3
Update in steps
Start with
balance = 100. Use+=to add 50,*=to double it, and-=to take away 30. Print after each step.
Code & diagrams
Before running, write down your prediction for each of the first five lines.
Expected output
14 20
-9 9
512 64
3 2.0
7
True True
15
12
24
6.0
36.0
3
1Compare floor, ceil, trunc and round for -2.5 on line 4.
Expected output
4.0 3.141592653589793 2.718281828459045
-3 -2 -2 -2
2 3
7 9 3 1024 0.5
6 120 5.0
10.0 3.0
1.4142135623730951 1.4142135623730951Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Square root of a negative number
Ask math.sqrt for the square root of -1.
import math
print(math.sqrt(-1))Break #2
Update a variable that does not exist
Use += on a name that was never created.
points += 1Myth vs fact
Myth
-3 ** 2 is 9.
Fact
** runs before the unary minus, so it is -(3 ** 2), which is -9. Write (-3) ** 2 for 9.
Myth
2 ** 3 ** 2 is 64.
Fact
Powers group from the right: 2 ** (3 ** 2) is 512.
Myth
x += 1 and x = x + 1 are always identical.
Fact
For numbers they are. For lists (Phase 4) += changes the list in place, while x = x + [..] builds a new one, and that difference matters.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The full precedence table in Python has about 18 levels. From tightest to loosest:
**, unary+ - ~,* / // % @,+ -, shifts<< >>,&,^,|, comparisons andin/is,not,and,or, the conditional expression,lambda, then:=. Two consequences interviewers test:1 + 2 << 3is(1 + 2) << 3(24), and, unlike C,x & 1 == 0means(x & 1) == 0because comparisons are looser than&. You do not need to memorise it: use parentheses wherever you doubt. - ▸
**has a few more rules. It binds tighter than a unary operator on its left (-3 ** 2is -9) but a unary operator on its right is fine (2 ** -1is 0.5). A negative base with a fractional exponent gives a complex number, not an error:(-8) ** (1 / 3)is(1.0000000000000002+1.7320508075688772j).0 ** 0is1, and0.0 ** -1raisesZeroDivisionError. For big modular powers use the three-argumentpow(base, exp, mod), which never builds the huge intermediate number (used in cryptography). - ▸
Operators call special methods:
a + bcallsa.__add__(b), and if that returnsNotImplementedPython triesb.__radd__(a). That is why+works on numbers, strings and lists, and why NumPy arrays can add element by element (Phase 8 and Phase 14). Forx += yPython tries__iadd__first: lists define it (in-place, same object), ints and strings do not, so they fall back tox = x + y. Ina[i] += 1the targeta[i]is evaluated once. - ▸
Since Python 3.8
math.dist,math.prod,math.isqrt(exact integer square root) andmath.combexist.math.isqrtis the safe choice for big ints, wheremath.sqrtwould round to a float:math.isqrt(10 ** 40)is exact. Python 3.9 addedmath.lcm. - ▸
Machine learning code does a lot of arithmetic on whole arrays. NumPy runs
a * b + con millions of numbers in compiled code, with the same precedence rules as here.
Remember this
- 1
From strongest (first) to weakest (last), the arithmetic order is:
**(power); unary minus-x; then*,/,//,%; then+and-. Comparisons (<,==) come after all arithmetic (Topic 1.8). Operators on the same level run left to right, so10 - 4 - 3is(10 - 4) - 3, which is 3, not 9. - 2
Parentheses override everything.
(2 + 3) * 4is 20 while2 + 3 * 4is 14. When a line is hard to read, add parentheses even if the precedence rules would give the same answer. Clear code beats clever code. - 3
Two surprises. First,
**is right-associative:2 ** 3 ** 2is2 ** (3 ** 2), which is 512, not 64. Second,**binds tighter than a minus sign on its left:-3 ** 2is-(3 ** 2), which is -9. To square minus three, write(-3) ** 2. - 4
Augmented assignment updates a variable using its old value.
score += 5meansscore = score + 5. The family:+=,-=,*=,/=,//=,%=,**=. They are shorter, and they put the name on the line only once, which avoids typos. Note that the variable must exist first:points += 1on a new name givesNameError. - 5
Python has built-in helpers for numbers:
abs(x)(distance from zero),min(a, b, ...),max(a, b, ...),pow(a, b)andround(x). Themathmodule (a ready-made toolbox of functions) addssqrt,floor,ceil,trunc,gcd,factorial,hypot,log2,log10and the constantsmath.piandmath.e. You load the module withimport math(Topic 6.1). - 6
math.floorgoes toward minus infinity,math.ceiltoward plus infinity,math.trunctoward zero, androundto the nearest even. For -2.5 they give -3, -2, -2 and -2. Most functions inmathreturn floats, somath.sqrt(16)is4.0. (floor,ceil,trunc,gcdandfactorialreturn ints.) They raiseValueErrorfor impossible input (math.sqrt(-1)).
Explain it without notes
State the order of operations for +, -, *, /, //, % and ** in Python.
Why is -3 ** 2 equal to -9, and what is 2 ** 3 ** 2?
What does x += 1 do, and when does it fail?
How do floor, ceil, trunc and round differ on -2.5?
Practice
Compute and print the average of the marks 72, 88 and 95, rounded to 2 decimal places.
Start with count = 3, then use augmented assignment to double it and then add 10. Print count.
Print the longest side (the hypotenuse) of a right triangle with sides 6 and 8, using the math module.
Trade-offs
- ↔
Relying on precedence vs. adding parentheses. The rules are reliable, but extra parentheses cost nothing and save the next reader a pause. Use them when a line mixes more than two kinds of operator.
- ↔
**
**vs.math.sqrt/math.pow.****works with ints exactly (2 ** 100);math.sqrt,math.powand friends return floats and can lose precision on big ints. Use each for what it is good at.
Done when you can
Done when you can evaluate
2 + 3 * 4 ** 2by hand and check it in Python.Done when you can explain
-3 ** 2and2 ** 3 ** 2.Done when you can use
+=,-=,*=and//=and know the variable must exist first.Done when you can use
abs,min,maxandmath.sqrt.Done when you can choose between
floor,ceil,truncandround.