This lesson gives the precedence rules that decide what an expression means, shows what the plus and star operators do to strings, explains what makes a comment worth writing, and names the three kinds of error that the rest of the course depends on telling apart.
Subject: Python · 65 slides · code lesson
Open the interactive version of this deck
Title
Python · Chapter 2 — Variables, expressions and statements
§2.5-2.8, pp. 11-13
Objectives
Five things, each one you can check yourself at an interpreter prompt.
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 11-13 — the pages these objectives are drawn from
Warm-up
You can answer this from ordinary arithmetic. The point is to notice how you decided.
Discussion prompt
What is the value of 6 + 4 / 2 — and, more importantly, what rule did you use to decide whether to divide first or add first? Write down the rule in your own words.
Hint: There are two candidate answers, 5 and 8, and only one rule chooses between them.
Answer:
The answer is 8.0: the division happens first, giving 2.0, and adding 6 gives 8.0. The decimal point is there because division always produces a float, from the previous lesson.
The rule you used is precedence — multiplication and division bind more tightly than addition and subtraction. Python follows mathematical convention here, so your existing instincts are mostly right.
Mostly is the interesting word. This lesson is about the places where the convention runs out and you need the actual rule, and about the book's own advice for when you cannot tell by looking.
Concept
When an expression contains more than one operator, the order of evaluation depends on the order of operations. Before Python computes anything, it decides what the expression MEANS — which operator applies to which values — and only then does the arithmetic.
precedence — The rule that decides which operator in an expression is applied first, and therefore what the expression means.
The acronym PEMDAS is a useful way to remember the rules: Parentheses, Exponentiation, Multiplication and Division, Addition and Subtraction. Python follows mathematical convention for mathematical operators, which is why most of your existing intuition transfers.
Figure (svg): A ladder showing the expression two times three minus one being grouped as a multiplication first and then a subtraction
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 11-12
Section
Section 1
Concept
There are four levels of precedence among the operators you have met, and the acronym PEMDAS names them in order.
Parentheses can also be used to make an expression easier to read, as in (minute * 100) / 60, even when they do not change the result. That is a real use, not a lesser one.
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 11-12
Picture it
Read the pyramid from the top: whatever is highest gets computed first.
Figure (svg): Four labelled boxes in order showing parentheses, then exponentiation, then multiplication and division, then addition and subtraction
The book's own advice about this table is worth quoting: I do not work very hard to remember the precedence of operators. If I cannot tell by looking at the expression, I use parentheses to make it obvious.
Worked example
Each one is chosen because the wrong answer is tempting. Predict all four before advancing.
>>> 2 * (3 - 1)
4
>>> 1 + 2 ** 3
9
>>> 2 * 3 ** 2
18
>>> 6 + 4 / 2
8.0| Expression | Which rule decides it | Value |
|---|---|---|
| 2 * (3 - 1) | parentheses first | 4, not 5 |
| 1 + 2 ** 3 | exponentiation before addition | 9, not 27 |
| 2 * 3 ** 2 | exponentiation before multiplication | 18, not 36 |
| 6 + 4 / 2 | division before addition | 8.0, not 5.0 |
Find the highest-precedence operator present.
Why: In each of these there is exactly one candidate for going first, which is why they are good examples.
Note the tempting wrong answer in each case.
Why: 27 comes from adding before raising to a power; 36 from multiplying before; 5.0 from adding before dividing. Each wrong answer is what you get from strict left-to-right reading.
Notice that strict left-to-right is wrong three times out of four.
Why: That is the whole reason precedence exists as a concept. Reading an expression the way you read a sentence gives the wrong answer more often than not.
Figure (svg): The state of the program after each line of Worked example the book's four examples, worked, drawn as a ladder with one rung per traced line
4, 9, 18 and 8.0. In every case the higher-precedence operator ran first, and in three of the four that differs from reading left to right.
Verify: Rewrite each with explicit parentheses and check the values are unchanged.
Why: 2 * ((3) - 1), 1 + (2 ** 3), 2 * (3 ** 2) and 6 + (4 / 2) give the same four answers. Adding parentheses that agree with precedence must not change anything — if a value shifts, your reading of the grouping was wrong, and this is the cheapest way to find that out.
Prediction
One of the two tempting answers comes from reading left to right.
Predict first
What is the value of 1 + 2 ** 3?
Correct: 9 — exponentiation binds tighter than addition, so it is 1 + 8.
Why: Exponentiation is on the second level of the pyramid and addition is on the fourth, so 2 ** 3 is computed first, giving 8, and adding 1 gives 9. The tempting 27 comes from adding first, getting 3, and cubing it — which is exactly what strict left-to-right reading would do, and exactly what precedence exists to override.
Worked example
Here the parentheses change nothing about the value. Decide whether they are worth writing.
>>> minute = 59
>>> minute * 100 / 60
98.33333333333333
>>> (minute * 100) / 60
98.33333333333333| Expression | How it groups | Result |
|---|---|---|
| minute * 100 / 60 | same precedence, so left to right | multiply, then divide |
| (minute * 100) / 60 | parentheses agree with what already happens | identical value |
| which to write | the second, for a reader | the value is not the only consideration |
Work out the grouping without the parentheses.
Why: Multiplication and division have the same precedence, so they are evaluated left to right: multiply by 100 first, then divide by 60.
Add the parentheses and check.
Why: They express exactly the grouping that already happens, so the value is unchanged.
Decide whether to keep them.
Why: The book keeps them. Parentheses can be used to make an expression easier to read even when they do not change the result, and a reader who does not have to work out the grouping cannot get it wrong.
Figure (svg): Two columns showing an expression without parentheses and with them, both producing the same value
Both forms give 98.33333333333333. The parentheses are redundant to Python and useful to a reader, which is a perfectly good reason to write them.
Verify: Try the parentheses in the OTHER position and watch the value change.
Why: minute * (100 / 60) gives 98.33333333333333 as well here — but that is a coincidence of these particular numbers, and with minute = 7 the two forms give 11.666666666666666 and 11.666666666666668. Finding a case where the grouping matters is what proves the parentheses were expressing a real choice.
Trap
A student writes a long expression, is fairly sure about the precedence, and leaves it unparenthesised to keep it tidy.
Treat parentheses as clutter
Why: Extra brackets do look noisier, and the expression is shorter without them.
Fairly sure is the problem. If the reading is wrong the program computes something confidently and silently, and there is no error to alert anybody.
The book's advice is unusually direct, and it is advice about you rather than about Python.
Do not work hard at memorising precedence
Why: The table exists so that expressions have a definite meaning, not so that you can recite it.
If you cannot tell by looking, add parentheses to make it obvious
Why: This costs two characters and removes the doubt permanently, for you and for every later reader.
Notice what this advice implies: an expression whose meaning depends on knowing an obscure precedence rule is a badly written expression, even when it is correct.
Ranking
Highest precedence first. Four levels, and everything you know sits on one of them.
Put in order
Why: PEMDAS names them in exactly this order: Parentheses, Exponentiation, Multiplication and Division, Addition and Subtraction. Parentheses are top because their whole purpose is to override everything else, and addition is bottom because it is the operation the others are usually feeding. Note that multiplication and division share a level, as do addition and subtraction — which is what makes the left-to-right rule in the next section necessary.
Fill the middle
The expression currently gives 5. Make it give 4 by adding parentheses only.
Fill in the blanks
2 * (3 - 1 # currently 2 * 3 - 1, which is 5; make it 4
Why: Writing 2 * (3 - 1) forces the subtraction to happen first, giving 2 * 2, which is 4. This is the first of the book's four examples and it shows what parentheses are FOR: not decoration, but a way to say that the default grouping is not the one you want. Nothing else in the expression changed — the operators and the numbers are identical, and only the meaning moved.
Socratic
A language could require parentheses everywhere. Argue about whether it should.
Discussion prompt
Imagine a version of Python with no precedence rules, where every expression with two or more operators had to be fully parenthesised. Name one thing that would get better and one that would get worse.
Hint: Think about how often you write an expression versus how often you read one.
Answer:
Better: no expression could ever be misread, because the grouping would always be stated. The whole category of precedence bugs would disappear.
Worse: ordinary arithmetic would become unreadable. ((a * b) + (c * d)) is harder to take in at a glance than a * b + c * d, and the conventional reading of the second is the one every reader already has.
Python's compromise is to follow mathematical convention, so that the common cases match what you already expect, and to make parentheses available for the cases where they do not. The book's advice is then the practical version: rely on convention where it is obvious, and parenthesise where it is not.
Section
Section 2
Concept
Operators with the same precedence are evaluated from left to right — except exponentiation. That single sentence resolves every remaining ambiguity, and it contains one exception worth knowing about.
>>> 10 - 3 - 2
5
>>> 100 / 5 * 2
40.0
>>> 2 ** 3 ** 2
512| Expression | How it groups | Value |
|---|---|---|
| 10 - 3 - 2 | left to right: (10 - 3) - 2 | 5, not 9 |
| 100 / 5 * 2 | left to right: (100 / 5) * 2 | 40.0, not 10.0 |
| 2 3 2 | the exception: right to left, 2 (3 2) | 512, not 64 |
The book's own example of this is degrees / 2 * pi: the division happens first and the result is multiplied by pi. To divide by two times pi you need parentheses, or you write degrees / 2 / pi.
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 12-12
Picture it
Read what it says, not what it looks like it means.
Figure (svg): Two columns showing what degrees slash two star pi actually computes against what a reader usually intends
Nothing here produces an error. This is a semantic error in waiting — the third kind, which the last section of this lesson names.
Worked example
Two readings, two different answers. Only one is Python's.
>>> 10 - 3 - 2
5
>>> (10 - 3) - 2
5
>>> 10 - (3 - 2)
9| Expression | Grouping | Value |
|---|---|---|
| 10 - 3 - 2 | no parentheses: left to right | 5 |
| (10 - 3) - 2 | the same grouping, stated | 5 |
| 10 - (3 - 2) | the other grouping | 9 |
Notice that the two groupings genuinely differ.
Why: Five and nine are not the same number, so the grouping is doing real work here — unlike with addition, where it would not matter.
Apply the rule.
Why: Both minus signs are on the same precedence level, so they are evaluated left to right, which means the first grouping.
Check that this matches ordinary arithmetic.
Why: It does: on paper you would also read 10 - 3 - 2 as seven minus two. Python is following the convention you already have.
Figure (svg): The state of the program after each line of Worked example subtraction is not associative, drawn as a ladder with one rung per traced line
5. Both operators are on the same level, so they run left to right, giving (10 - 3) - 2. The other grouping would give 9 and requires explicit parentheses to obtain.
Verify: Try the same thing with addition and see the difference vanish.
Why: 10 + 3 + 2 gives 15 whichever way it groups, so the left-to-right rule is invisible there. The rule only shows itself with operators where grouping matters — subtraction and division — which is precisely where you need to know it.
Prediction
Two operators, one level. Apply the rule rather than your instinct about which looks more important.
Predict first
What is the value of 100 / 5 * 2?
Correct: 40.0 — the division and multiplication are on the same level, so they run left to right: 100 divided by 5 is 20, times 2 is 40.
Why: The tempting answer is 10.0, which comes from treating 5 * 2 as a denominator. Nothing in the expression says that: the star does not group anything, and both operators are on the same precedence level, so the left one goes first. If you meant to divide by ten, you needed parentheses — and this is the same trap as the book's degrees over two pi, wearing different numbers.
Worked example
This is the one place the left-to-right rule does not apply. Predict before advancing.
>>> 2 ** 3 ** 2
512
>>> (2 ** 3) ** 2
64
>>> 2 ** (3 ** 2)
512| Expression | How it groups | Value |
|---|---|---|
| 2 3 2 | exponentiation groups RIGHT to left | 2 ** 9 |
| (2 3) 2 | forced left grouping | 8 ** 2, which is 64 |
| 2 (3 2) | the default, stated explicitly | 2 ** 9, which is 512 |
Apply the general rule and get the wrong answer.
Why: Left to right would give (2 3) 2, which is 64. That is not what Python produces.
Apply the exception.
Why: Exponentiation is the one operator that groups right to left, so the expression means 2 (3 2), which is 2 ** 9.
Check the arithmetic.
Why: Three squared is nine, and two to the ninth is 512.
Figure (svg): A ladder showing two double-star three double-star two reducing right to left through two double-star nine to five hundred and twelve
512. Exponentiation groups right to left, so the rightmost power is computed first — unlike every other operator you have met.
Verify: Ask which grouping matches how the expression would be written on paper.
Why: Written as a tower of exponents, the top of the tower is evaluated first, which is the right-to-left reading. Python's exception is not arbitrary — it matches the mathematical convention for stacked exponents, which is why it is the one operator that gets an exception.
Trap
A formula on paper has a horizontal bar: degrees over two pi. A student types it as degrees / 2 * pi, reading the bar as a slash and the rest straight across.
Translate the visual layout rather than the grouping
Why: The bar on paper groups everything beneath it, and a slash groups nothing at all.
The result is (degrees / 2) * pi, which for degrees = 360 gives about 565 instead of about 57. No error appears, and the number looks plausible enough to use.
Translate the GROUPING that the layout expresses, not the symbols.
Ask what the bar is grouping
Why: Everything below it, which means the whole denominator needs parentheses: degrees / (2 * pi).
Or divide twice, which needs no parentheses at all
Why: degrees / 2 / pi is the same thing, and the book offers it as an alternative.
Then check with a number you can verify by hand. For degrees = 360 the answer should be about 57, and a result of 565 tells you immediately which reading Python took.
Discrimination
For some operators the grouping changes the answer. For others it cannot.
Sort into buckets
For each expression, does changing the grouping change the value?
Reverse engineer
Each line shows a value that differs from the unparenthesised default. Restore the parentheses.
Fill in the blanks
10 - (3 - 2) # gives 9, not 5
100 / **(5 * 2)** # gives 10.0, not 40.0
Why: To get 9 from the first, the subtraction of 2 from 3 has to happen first, so the parentheses wrap 3 - 2. To get 10.0 from the second, the 5 * 2 has to become the denominator, so the parentheses wrap 5 * 2. In both cases the parentheses are overriding the left-to-right rule, which is the only way to override it — there is no other syntax for saying group these two instead.
Edge cases
Exponentiation groups right to left. Check whether any other operator does.
Discussion prompt
Work out, by predicting and then reasoning, whether 8 / 4 / 2 groups left to right or right to left — and say what value each grouping would give.
Hint: One grouping gives 1.0 and the other gives 4.0.
Answer:
Left to right gives (8 / 4) / 2, which is 2 / 2, which is 1.0. Right to left would give 8 / (4 / 2), which is 8 / 2, which is 4.0.
Python gives 1.0, so division groups left to right like everything else. Exponentiation really is the sole exception.
It is worth checking rather than assuming, because the exception is easy to over-generalise into some operators go right to left. The accurate version is narrower: exponentiation does, and nothing else in this course does.
Section
Section 3
Concept
In general you cannot perform mathematical operations on strings, even if the strings look like numbers. But there are two exceptions: plus and star.
concatenation — Joining two strings end to end, which is what the plus operator does when both operands are strings.
>>> first = 'throat'
>>> second = 'warbler'
>>> first + second
'throatwarbler'
>>> 'Spam' * 3
'SpamSpamSpam'| Expression | What the operator does | Result |
|---|---|---|
| first + second | concatenation: joins them end to end | 'throatwarbler' |
| 'Spam' * 3 | repetition: repeats the string | 'SpamSpamSpam' |
| 'chinese' - 'food' | no meaning is defined | TypeError |
For the star, if one of the values is a string, the other has to be an integer — repeating a string a fractional number of times has no meaning, and Python declines to invent one.
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 12-12
Picture it
Neither of these is arithmetic. Both are ways of building a longer string out of shorter ones.
Figure (svg): The strings throat and warbler drawn as adjacent boxes forming the single string throatwarbler
The result is a single new string. Neither of the originals was changed — a point chapter 8 makes formally when it says strings are immutable.
Worked example
The book asks you to see the analogy. Follow it and then find where it breaks.
>>> 'Spam' * 3
'SpamSpamSpam'
>>> 'Spam' + 'Spam' + 'Spam'
'SpamSpamSpam'
>>> 4 * 3
12
>>> 4 + 4 + 4
12| Expression | How it relates to the operator below | Result |
|---|---|---|
| 4 * 3 | repeated addition | 12 |
| 'Spam' * 3 | repeated concatenation | 'SpamSpamSpam' |
| the analogy | star is to plus as it always was | consistent |
State the arithmetic fact.
Why: Multiplication by a whole number is repeated addition: 4 * 3 is 4 + 4 + 4.
Apply the same relationship to strings.
Why: If plus concatenates, then star should repeat, and 'Spam' * 3 should be 'Spam' + 'Spam' + 'Spam'.
Check that Python agrees.
Why: It does. The use of plus and star for strings makes sense by analogy with addition and multiplication, and the analogy is exact in this respect.
Figure (svg): The state of the program after each line of Worked example why repetition makes sense, drawn as a ladder with one rung per traced line
Both expressions give 'SpamSpamSpam'. Repetition stands to concatenation exactly as multiplication stands to addition, which is why these two operators were the ones chosen for strings.
Verify: Check the requirement on the second operand.
Why: 'Spam' * 3.0 is an error, because you cannot repeat something 3.0 times. The analogy demands a whole number of repetitions, and Python enforces exactly that — which is evidence the analogy is the actual design principle rather than a story told afterwards.
Prediction
Star works on strings. Both of these are strings. Reason it through anyway.
Predict first
What happens when Python evaluates '3' * '4'?
Correct: A TypeError, because repetition needs an integer for the count and both operands here are strings.
Why: The rule is precise: if one of the values is a string, the other has to be an integer. Two strings do not satisfy it, because there is no sensible meaning for repeating something a string number of times. Note how narrowly the error is targeted — '3' * 4 works and gives '3333', and 3 * '4' works too. It is having strings on BOTH sides that fails.
Worked example
The book asks a question and leaves it hanging. This is the answer.
>>> 3 + 4
7
>>> 4 + 3
7
>>> 'throat' + 'warbler'
'throatwarbler'
>>> 'warbler' + 'throat'
'warblerthroat'| Expression pair | Results | What it shows |
|---|---|---|
| 3 + 4 and 4 + 3 | both give 7 | addition is commutative |
| 'throat' + 'warbler' | 'throatwarbler' | one order |
| 'warbler' + 'throat' | 'warblerthroat' | a different string |
Test the property on numbers.
Why: Addition is commutative: swapping the operands does not change the sum. Three plus four and four plus three are both seven.
Test the same property on strings.
Why: Swapping the operands gives a completely different string. 'throatwarbler' and 'warblerthroat' are not equal.
Name the property that is missing.
Why: Concatenation is not commutative. This is the answer to the book's question: addition has a property that string concatenation does not.
Figure (svg): Two columns comparing addition and concatenation, showing that swapping operands changes one and not the other
Commutativity. Addition gives the same result whichever order the operands are in; concatenation does not, because the order of the characters is the whole content of a string.
Verify: Check whether the other associativity-style properties survive.
Why: Concatenation IS associative — grouping three strings either way gives the same result — so the analogy fails on exactly one property rather than collapsing. Knowing which property fails is more useful than a vague sense that strings are different.
Error analysis
One of these works. Mark what is wrong with the other three and name the error each would give.
Annotate
Notice that all three failures are TypeErrors, not syntax errors. The lines are well-formed Python; they ask for something no operator is defined to do.
Counterexample
You have seen that it usually is not. Push on the word usually.
Discussion prompt
Find two strings a and b, not equal to each other, for which a + b and b + a give the same result. Then say whether that makes concatenation commutative.
Hint: What if one of them is empty?
Answer:
Take a as 'hello' and b as the empty string. Both a + b and b + a give 'hello', and the two strings are not equal to each other.
Another family: any two strings made of the same single repeated character, such as 'aa' and 'aaa' — both orders give 'aaaaa'.
This does not make concatenation commutative. A property holds for an operation only if it holds for EVERY pair, and 'throat' with 'warbler' is a counterexample. Finding cases where a broken property happens to hold is a genuinely useful habit — it stops you from over-claiming in the other direction.
Sorting
Two rules decide all six: plus needs two strings, star needs a string and an integer.
Sort into buckets
Sort each expression.
Translation
The same symbol, four different jobs, chosen by the types on either side.
Match the pairs
Why: Two operators and four meanings, selected entirely by the types of the operands. This is the idea from the previous lesson — an operator's meaning depends on the types it is given — now with a second example, and it is worth noticing that the two string meanings were chosen so that the analogy with arithmetic holds: star is to plus for strings exactly as it is for numbers.
Section
Section 4
Concept
As programs get bigger they get more difficult to read. Formal languages are dense, and it is often difficult to look at a piece of code and figure out what it is doing, or why. Comments are notes in natural language that explain the program, and they start with the hash symbol.
comment — A note in a program, written for a human reader, that Python ignores completely.
# compute the percentage of the hour that has elapsed
percentage = (minute * 100) / 60
percentage = (minute * 100) / 60 # percentage of an hour| Line | What it is | What Python does with it |
|---|---|---|
| line 1 | a comment on a line by itself | ignored entirely |
| line 2 | the statement it describes | runs normally |
| line 4 | a comment at the end of a line | the code before it runs; the comment is ignored |
Everything from the hash to the end of the line is ignored — it has no effect on the execution of the program. A comment may sit on its own line or at the end of a line of code.
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 12-13
Picture it
The difference is not length or effort. It is whether the comment adds anything the code does not already say.
Figure (svg): Two columns contrasting a redundant comment that restates the code with a useful one that adds the unit
Comments are most useful when they document non-obvious features of the code. It is reasonable to assume the reader can figure out what the code does; it is more useful to explain why.
Worked example
The comment here is not wrong. It is merely worthless. Improve it.
total = total * 1.2 # multiply total by 1.2
total = total * 1.2 # add 20% VAT| Version | What the comment supplies | Verdict |
|---|---|---|
| the first comment | restates the operator and the number | adds nothing |
| the second comment | says what 1.2 MEANS | adds the reason |
| the test | could a reader work this out from the code alone? | no — 1.2 is opaque |
Ask what the code already says.
Why: It says that total is multiplied by 1.2. Any reader who knows Python can see that, so a comment saying it adds nothing.
Ask what the code cannot say.
Why: It cannot say why 1.2. The number is a rate, and which rate, and what it is a rate OF, are invisible.
Write the comment about that.
Why: Adding 20 percent VAT is information not in the code, and it is the thing a later reader will need.
Figure (svg): The state of the program after each line of Worked example improving a comment, drawn as a ladder with one rung per traced line
The second version. The first restates the code in English; the second explains the one thing the code cannot express, which is what the magic number 1.2 means.
Verify: Imagine the rate changing to 1.25 and check which comment survives.
Why: The first comment becomes false immediately and silently — it says 1.2 and the code says 1.25. The second stays true if the rate changed and becomes false only if the TAX changed, which is a much rarer event. A comment that goes stale less often is a better comment.
Elimination
All four are accurate today. Only one is worth the line.
Eliminate the wrong options
The line is: wait = 3. Which comment survives?
Survives elimination: C
Why: C carries two things the code cannot: the unit (seconds) and the reason (the API rejects faster polling). Both are facts about the world rather than about the syntax, so neither can be recovered by reading the line, and neither goes stale when the 3 is tuned to 4. That is the test for a comment worth writing — does it say something the code cannot, about something that changes more slowly than the code?
Worked example
Good variable names reduce the need for comments. But not to zero, and not for free.
# elapsed time in seconds
t = 90
elapsed_seconds = 90
the_number_of_elapsed_seconds_since_start = 90| Version | Where the meaning lives | Cost |
|---|---|---|
| t with a comment | the name says nothing; the comment carries it all | comment can drift from the name |
| elapsed_seconds | the name carries the meaning | no comment needed |
| the very long name | meaning carried, readability lost | expressions become unreadable |
Consider the comment-carried version.
Why: It works, but the meaning lives somewhere Python does not check, and every later use of t is uninformative.
Consider the well-named version.
Why: Good variable names can reduce the need for comments, and this one removes it entirely — the meaning travels with every use of the name.
Consider the over-named version.
Why: Long names can make complex expressions hard to read, so there is a tradeoff. An expression with three names this long is worse than one with a comment.
Figure (svg): Three boxes showing meaning carried by a comment, by a name, and by an over-long name, with the cost of each
The middle version. A good name puts the meaning where every reader will see it, on every line; the comment version hides it at the top, and the very long name buys meaning at a price that complex expressions cannot pay.
Verify: Write a two-operator expression with each of the three names and compare.
Why: t * 2 + 5 says nothing; elapsed_seconds * 2 + 5 reads cleanly; the long version overflows the line and obscures the arithmetic. The tradeoff is only visible in expressions, which is exactly where the book says it bites.
Trap
A line has a comment describing exactly what it does. Six months later the line is edited and the comment is not.
Write comments that duplicate the code
Why: It feels thorough, and at the moment of writing the comment is perfectly accurate.
Now the program has two accounts of what it does, and they disagree. A reader who trusts the comment is worse off than one who had no comment at all.
Write comments about things that change more slowly than the code.
Comment the WHY, not the what
Why: Why a rate is 1.2, why this order matters, why the obvious approach was rejected. These outlive edits to the line.
Prefer a better name to a comment where you can
Why: A name cannot drift out of date relative to the code, because it IS the code.
Comments are most useful when they document non-obvious features of the code. A comment that restates the obvious is not neutral — it is a future lie with a due date.
Prediction
The hash can appear anywhere on a line. Work out what that means.
print('a') # print('b')| Part of the line | Before or after the hash | Effect |
|---|---|---|
| print('a') | before the hash, so it runs | displays a |
| # print('b') | after the hash, so it is ignored | displays nothing |
| result | one line of output | a |
Predict first
What does this line display?
Correct: Just a — everything from the hash to the end of the line is ignored, including something that looks like perfectly good code.
Why: Everything from the hash to the end of the line is ignored, with no exception for text that happens to be valid Python. This is exactly why commenting-out is a useful technique: putting a hash in front of a line disables it without deleting it. It is also why a stray hash is a confusing bug — the line still looks like it does something.
Two truths and a lie
Two of these are true. Keep the lie.
Eliminate the wrong options
Rule out the two true statements.
Survives elimination: C
Why: C is the lie. Comments have no effect on the execution of the program whatsoever — they are discarded before the program runs, not skipped over while it runs. The belief that comments cost something at run time is common and leads people to write fewer of them than they should, which is why it is worth killing explicitly.
Explain it to yourself
The advice sounds like a slogan. Make it concrete.
Discussion prompt
The book says it is reasonable to assume the reader can figure out what the code does, so it is more useful to explain why. Give one example from your own experience — in any field — where knowing WHAT was done did not help, and knowing WHY would have.
Hint: Anything you have inherited from someone else: a spreadsheet, a recipe, a set of instructions at work.
Answer:
A common example: a spreadsheet cell that subtracts 3 from a total. You can see what it does. Nobody can tell you why, so nobody dares remove it, and it survives for years after the reason has gone.
The same thing happens in code constantly. A line that looks pointless is nearly always there for a reason somebody has forgotten, and the comment that would have saved everybody is one sentence long.
This is also why why comments age better. The reason a line exists changes far less often than the details of how it does it — so a comment about the reason stays true through edits that would falsify a comment about the mechanism.
Section
Section 5
Concept
Three kinds of error can occur in a program: syntax errors, runtime errors and semantic errors. It is useful to distinguish between them in order to track them down more quickly.
Runtime errors are rare in the simple programs of the first few chapters, so it might be a while before you meet one. Semantic errors are not rare at all, and identifying them is tricky because it requires you to work backward from the output.
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 13-13
Picture it
The three kinds differ in WHEN they appear, and that is the fastest way to tell them apart.
Figure (svg): A flow chart showing a program being checked for syntax, then run, then its output compared with what was intended, with a different error kind branching off at each stage
Note the asymmetry: the first two announce themselves and the third does not. That is why the third is the dangerous one, and why so much of this course is about predicting output before running it.
Worked example
Identify the kind of error in each before advancing. The symptom is the clue.
print('total: ', 8))
print(10 / 0)
print(9 / 5 * 100 + 32)| Line | What happens | Which kind of error |
|---|---|---|
| line 1 | an unmatched closing parenthesis | syntax error: nothing runs |
| line 2 | parses fine, fails when it runs | runtime error: ZeroDivisionError |
| line 3 | runs fine, prints 212.0 | semantic error if you meant to convert 100 C |
Check whether the program can run at all.
Why: The first line cannot: parentheses have to come in matching pairs, and there is an extra closing one. Python displays a message and quits.
Check whether it finishes once started.
Why: The second line parses perfectly and then fails while running, because dividing by zero has no answer. That is a runtime error, also called an exception.
Check whether the output is what you wanted.
Why: The third runs to completion and prints 212.0. Nothing is wrong with it unless you meant to convert 100 degrees Celsius — in which case the formula should have been 100 * 9 / 5 + 32, and 9 / 5 * 100 + 32 is the same thing, so this one is actually correct.
Figure (svg): The state of the program after each line of Worked example three broken programs, one of each kind, drawn as a ladder with one rung per traced line
A syntax error, a runtime error, and a program that is fine. The third is there to make the point that you cannot identify a semantic error from the code alone — you have to know what was intended.
Verify: Ask what extra information the third case needed.
Why: Only the intention. Syntax and runtime errors are properties of the program; a semantic error is a mismatch between the program and somebody's intent, which is why no tool can find one for you and why it requires working backward from the output.
Definition probe
Diagnose from the symptom alone, without seeing the code.
Sort into buckets
Sort each symptom by the kind of error it indicates.
Worked example
This program runs, prints a number, and is wrong. Find out how.
degrees = 360
radians = degrees / 2 * 3.14159
print(radians)| Line | What happens | Result |
|---|---|---|
| degrees = 360 | assignment | degrees -> 360 |
| degrees / 2 * 3.14159 | left to right: (360 / 2) * 3.14159 | 565.4862 |
| print(radians) | displays it | 565.4862, which is nonsense |
Confirm there is no syntax or runtime error.
Why: The program parses and runs to completion. Both of the first two gates were passed, so whatever is wrong is a semantic error by definition.
Check the output against something you know.
Why: 360 degrees is about 6.28 radians. The program printed 565, which is out by a factor of ninety.
Find the cause in the precedence rule.
Why: The expression means (degrees / 2) * 3.14159 because same-precedence operators run left to right. The intended meaning was degrees / (2 * 3.14159).
Figure (svg): A panel listing the three error kinds with the symptom that identifies each
The program computes (360 / 2) * pi instead of 360 / (2 * pi). It is a semantic error: nothing is wrong with the syntax or the running, and the answer is simply not what was meant.
Verify: Fix the grouping and check against the known value.
Why: degrees / (2 * 3.14159) gives 57.29..., and 360 degrees really is about 57.3 radians per... no — 360 degrees is 6.28 radians, so this reveals a second problem: the formula itself should be degrees * pi / 180. Checking against a value you know independently catches BOTH errors, which is exactly why that check is worth more than re-reading the code.
Trap
A program runs, prints a number, and exits cleanly. The student concludes it works.
Use the absence of an error message as the test of correctness
Why: Two of the three error kinds do produce messages, so the reasoning is right two-thirds of the time.
But a semantic error produces no message at all. The program runs without generating error messages, and it does not do the right thing — it does what you told it to do.
Check the output against something you knew before you ran the program.
Predict a value you can verify independently
Why: Convert 100 Celsius, and check the answer is 212. Compute 360 degrees in radians and check it is about 6.28.
Treat a plausible-looking wrong number as the expected failure mode
Why: Semantic errors rarely produce absurd output. They produce output that looks fine until you check it.
This is why the course keeps asking you to predict output before running. A prediction is a test you can run on your own understanding, and it is the only test that catches the third kind of error.
Prediction
The book says one of the three is rare in the first few chapters. Reason about which.
Predict first
Which kind of error will you meet least often in the next few chapters?
Correct: Runtime errors — the book says they are rare in the simple programs of the first few chapters.
Why: Early programs mostly compute and print, with no user input, no files and no division by values that might be zero, so there is very little that can go wrong once they have started. Syntax errors, by contrast, are most common exactly at the beginning: the book warns that during the first few weeks of your programming career you might spend a lot of time tracking them down. And semantic errors never become rare — they become subtler.
Matching
The three kinds need three different techniques, and knowing which one to reach for is the payoff of the taxonomy.
Match the pairs
Why: Each technique matches what the error kind gives you. A syntax error gives you a location and no execution, so you inspect the text. A runtime error gives you a location AND a running program, so you ask about values. A semantic error gives you neither a location nor a message, so the only handle is the output — which is why it requires working backward and why it is the hardest of the three.
Real world
This is the most immediately useful idea in the chapter. Put it to work.
Discussion prompt
You have written thirty lines and something is wrong. Describe, in order, the three questions you would ask to place the problem in one of the three categories — and say what each answer rules out.
Hint: The questions are about symptoms, and you can answer them all before reading any code.
Answer:
First: did it run at all? If Python refused and quit, it is a syntax error, and only the reported line and its neighbours matter. Nothing about your logic is in question yet.
Second: did it stop partway with a message? Then it is a runtime error, the traceback names the line, and the question is what value that line received rather than whether it is written correctly.
Third: did it finish and give a wrong answer? Then it is semantic, no tool will help, and you work backward from the output — which is where print statements and predicted values earn their keep.
Each question rules out about a third of the possibilities before you have read a single line, which is exactly what the book means by saying the distinction helps you track errors down more quickly.
Comparison
Fill the blanks from memory. This table is the most portable thing in the chapter.
Comparison matrix
| Kind | When it appears | How you find it |
|---|---|---|
| syntax error | before anything runs — Python quits | read the reported line and check bracket and quote pairs |
| runtime error | partway through, with a message | read the traceback for the line, then ask what value it got |
| semantic error | never — the program finishes cleanly | compare the output with a value you knew independently |
The bottom-left cell is the whole problem. An error that never announces itself can only be caught by someone who had an expectation.
Pattern
Four sections of housekeeping, collapsed into one routine you can apply to any line you write.
Step 6 is the only one that catches the third kind of error, and it is the one people skip. A program that has never been run on an input with a known answer has not been tested at all.
Python documentation — Expressions Expressions
Check
Two operators on different levels. Which goes first?
>>> 2 * 3 ** 2
18| Sub-expression | Why it runs when it does | Value |
|---|---|---|
| 3 ** 2 | exponentiation is higher than multiplication | 9 |
| 2 * 9 | then the multiplication | 18 |
| result | not 36 | which would need (2 * 3) ** 2 |
Check your understanding
Why is 2 * 3 ** 2 equal to 18 rather than 36?
Answer: B
Why: Exponentiation sits on the second level of the precedence pyramid and multiplication on the third, so 3 ** 2 is evaluated first, giving 9, and 2 * 9 gives 18. Getting 36 would require the multiplication to happen first, which needs explicit parentheses: (2 * 3) ** 2.
Check
Two rules cover every case: plus needs two strings, star needs a string and an integer.
Check your understanding
Which of these produces a TypeError?
Answer: C
Why: Plus concatenates two strings, and it is not defined for a string and an int — Python will not guess whether you meant to join them or to add. The other three are all legal: repetition works with the string on either side, and concatenation works when both operands are strings.
Check
Diagnose from the symptom, exactly as the taxonomy lets you.
Check your understanding
A program runs to completion, prints no error message, and reports that the average of 10, 20 and 30 is 60. Which kind of error is this?
Answer: C
Why: The program ran without generating any error message and did not do the right thing. That is the definition of a semantic error. The likely cause is a missing division, or a division placed outside the sum — but the point of the taxonomy is that you can classify it from the symptom before you have any idea of the cause.
Real world
The three-kind taxonomy applies to anything written for a literal reader.
Discussion prompt
Take a form you have filled in, a recipe, or a set of instructions. Give an example of each of the three failures: one that makes it impossible to follow at all, one that stops you halfway, and one that you follow perfectly and still get the wrong result.
Hint: The third is the interesting one, and it is always a mismatch between the instructions and what was meant.
Answer:
Impossible to follow: a form field with no box, or a recipe step that references an ingredient not on the list. You cannot even begin — a syntax error.
Stops you halfway: fold in the egg whites when you used all the eggs in step two. You got some of the way and then hit something impossible — a runtime error.
Followed perfectly, wrong result: the recipe says teaspoons where it meant tablespoons. Every instruction was clear and you obeyed all of them, and the cake is wrong. That is a semantic error, and the only way to catch it is to have known roughly what the cake should taste like.
Commit first
Answer, then rate your confidence. This one has caught professionals.
Predict first
What is the value of 2 3 2?
Correct: 512 — exponentiation is the one operator that groups right to left, so it means 2 (3 2), which is 2 ** 9.
Why: Every other operator in this course groups left to right, which would give (2 3) 2, or 64. Exponentiation is the stated exception, matching the mathematical convention for a tower of exponents where the top is evaluated first. If you answered 64 you applied the general rule correctly and missed the exception — which is exactly why the book states the exception in the same sentence as the rule, and why the honest response to meeting this in real code is to add parentheses.
Explain it
The taxonomy is the most useful thing you can hand another beginner.
Discussion prompt
A classmate says my program is broken. Before looking at their code, ask them the three questions that will tell you which kind of error it is — and explain to them why you are asking each one.
Hint: All three questions are about symptoms, and none requires seeing the program.
Answer:
Ask: did it run at all? Then: did it stop partway with a message? Then: is the output wrong in a specific way you can describe?
Explain that each answer eliminates a category and points at a different technique — inspect the text, read the traceback, or compare against a known value.
The reason this is worth rehearsing is that it works before you have read any code, which means it also works on your own programs at the moment when you are most tempted to start changing lines at random.
Exit ticket
One honest answer. It decides what the next lesson opens with.
Predict first
Which of these is still least solid for you?
Correct: Whichever you picked is the right answer — this one is for you, not for a mark.
Why: These four have very different shelf lives. Precedence stops being an issue the moment you take the book's advice and parenthesise whenever you are unsure, which is a habit rather than a memory. The string operators are two rules and settle quickly. Comment quality is a judgement that keeps improving for years. And the error taxonomy is the one you will use several times a day for the rest of your programming life — Appendix A expands it into a whole chapter, and chapter 6 builds a development method around avoiding the third kind.
Connect it up
One page, no code, from memory.
Draw it
Draw a diagram of a line of code being processed, with three gates along it: does it parse, does it run, is the output right. Label the failure at each gate with the error kind and the technique for finding it. Then, above the first gate, add a small box for the two things that decide what an expression MEANS before any of this happens — precedence and types — with one example each of a line whose meaning surprised you.
Recap
Two pages, and chapter 2 is finished: names, values, meaning and the ways it can go wrong.
| If you remember one thing | It is this |
|---|---|
| From precedence | If you cannot tell by looking, use parentheses. Do not rely on memory. |
| From left-to-right | degrees / 2 * pi does not divide by two pi. It divides by two, then multiplies. |
| From strings | Concatenation is not commutative. Order is the whole content of a string. |
| From comments | Comment the why. The what is already written directly below. |
| From the taxonomy | No error message is not the same as no error. |
Chapter 3 is where you stop using functions somebody else wrote and write one of your own — which brings the stack diagram, the idea of a local variable, and the first real answer to why programs are built out of parts.
Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 11-13 — everything on these slides traces back here
Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.