2b Order of Operations, String Operations, and Comments

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

What this lesson covers

The lesson, slide by slide

1. Lesson 2b Order of Operations, String Operations, and Comments

Title

Python · Chapter 2 — Variables, expressions and statements

§2.5-2.8, pp. 11-13

2. By the end of this lesson you can

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

3. Before we start: what does this expression mean?

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.

4. The one idea behind this lesson: meaning is decided before it is computed

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

Rung two is the meaning. Rungs three and four are the arithmetic.

Think Python, 2nd edition — Allen B. Downey §2.5-2.8, pp. 11-12

5. PEMDAS: which operator goes first

Section

Section 1

6. The four levels, highest first

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

7. Picture it: four levels, and everything falls out of them

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

Four levels. Everything you have met so far is on one of them.

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.

8. Worked example: the book's four examples, worked

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
ExpressionWhich rule decides itValue
2 * (3 - 1)parentheses first4, not 5
1 + 2 ** 3exponentiation before addition9, not 27
2 * 3 ** 2exponentiation before multiplication18, not 36
6 + 4 / 2division before addition8.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

The whole run at once: each drop is one line of the program.

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.

9. Predict: 1 + 2 3

Prediction

One of the two tempting answers comes from reading left to right.

Predict first

What is the value of 1 + 2 ** 3?

  • 27
  • 9
  • 7
  • 12

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.

10. Worked example: parentheses used only for readability

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
ExpressionHow it groupsResult
minute * 100 / 60same precedence, so left to rightmultiply, then divide
(minute * 100) / 60parentheses agree with what already happensidentical value
which to writethe second, for a readerthe 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

Same value. Different amount of work for whoever reads it next.

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.

11. Trap: trusting your memory of the table over a pair of parentheses

Trap

The 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 fix

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.

12. Rank: put these operators in precedence order

Ranking

Highest precedence first. Four levels, and everything you know sits on one of them.

Put in order

  1. parentheses
  2. exponentiation
  3. multiplication and division
  4. addition and subtraction

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.

13. Fill the middle: force a different grouping

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.

14. Think it through: why have precedence at all?

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.

15. Same precedence: left to right, and the one exception

Section

Section 2

16. What happens when two operators are on the same level

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
ExpressionHow it groupsValue
10 - 3 - 2left to right: (10 - 3) - 25, not 9
100 / 5 * 2left to right: (100 / 5) * 240.0, not 10.0
2 3 2the 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

17. Picture it: the trap in degrees / 2 * pi

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

Both are legal. Only one is what the writer usually wanted.

Nothing here produces an error. This is a semantic error in waiting — the third kind, which the last section of this lesson names.

18. Worked example: subtraction is not associative

Worked example

Two readings, two different answers. Only one is Python's.

>>> 10 - 3 - 2
5
>>> (10 - 3) - 2
5
>>> 10 - (3 - 2)
9
ExpressionGroupingValue
10 - 3 - 2no parentheses: left to right5
(10 - 3) - 2the same grouping, stated5
10 - (3 - 2)the other grouping9

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

The whole run at once: each drop is one line of the program.

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.

19. Predict: 100 / 5 * 2

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?

  • 10.0
  • 40.0
  • 1000.0
  • An error

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.

20. Worked example: the exponentiation exception

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
ExpressionHow it groupsValue
2 3 2exponentiation groups RIGHT to left2 ** 9
(2 3) 2forced left grouping8 ** 2, which is 64
2 (3 2)the default, stated explicitly2 ** 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

The only operator in this course that reduces from the right.

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.

21. Trap: writing a formula the way it looks on paper

Trap

The 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.

The fix

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.

22. Discriminate: does the grouping matter here?

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?

grouping changes the value
10 - 3 - 2; 100 / 5 * 2; 8 / 4 / 2
grouping cannot change the value
1 + 2 + 3; 2 * 3 * 4; 5 + 6 + 7
matters
Subtraction and division are not associative: taking away a difference is not the same as taking away each part, and dividing by a quotient is not dividing twice. These are exactly the expressions where the left-to-right rule earns its keep.
safe
Addition and multiplication are associative, so any grouping gives the same answer. The left-to-right rule still applies — it just makes no observable difference, which is why nobody notices it until they meet a minus sign.

23. Reverse engineer: where did the parentheses go?

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.

24. Push the boundary: does the exception apply to anything else?

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.

25. String operations: what plus and star do to text

Section

Section 3

26. Two operators that work on strings, and several that do not

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'
ExpressionWhat the operator doesResult
first + secondconcatenation: joins them end to end'throatwarbler'
'Spam' * 3repetition: repeats the string'SpamSpamSpam'
'chinese' - 'food'no meaning is definedTypeError

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

27. Picture it: joining and repeating

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 two strings are placed end to end. Nothing is added.

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.

28. Worked example: why repetition makes sense

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
ExpressionHow it relates to the operator belowResult
4 * 3repeated addition12
'Spam' * 3repeated concatenation'SpamSpamSpam'
the analogystar is to plus as it always wasconsistent

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

The whole run at once: each drop is one line of the program.

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.

29. Predict: what does '3' * '4' do?

Prediction

Star works on strings. Both of these are strings. Reason it through anyway.

Predict first

What happens when Python evaluates '3' * '4'?

  • It gives '12', multiplying the numbers and converting back
  • It gives '3333', repeating the first four times
  • A TypeError, because repetition needs an integer count
  • It gives '34', joining them

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.

30. Worked example: where the analogy breaks

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 pairResultsWhat it shows
3 + 4 and 4 + 3both give 7addition 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

The one property the analogy does not carry across.

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.

31. Error analysis: four attempted string operations

Error analysis

One of these works. Mark what is wrong with the other three and name the error each would give.

Annotate

  • Line 1 subtracts two strings. Nothing sensible could be meant by removing one piece of text from another in general, so Python defines no meaning for it and reports a TypeError.
  • Line 2 divides two strings, which is even less meaningful. Same outcome: unsupported operand types.
  • Line 3 is the interesting one. Star DOES work on strings, but only with an integer on the other side — repeating a string 'a charm' times has no meaning.
  • So line 3 fails not because star is forbidden for strings but because both operands are strings. If one of the values is a string, the other has to be an integer.
  • Line 4 is the legal case: a string and an integer, giving 'thirdthirdthird'.
  • The rule underneath all four: in general you cannot perform mathematical operations on strings, even if the strings look like numbers, and the exceptions are plus with two strings and star with a string and an int.

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.

32. Find the counterexample: is concatenation ever commutative?

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.

33. Sort: legal or a TypeError?

Sorting

Two rules decide all six: plus needs two strings, star needs a string and an integer.

Sort into buckets

Sort each expression.

legal
'ab' + 'cd'; 'ab' * 3; 3 * 'ab'
TypeError
'ab' + 3; 'ab' - 'a'; 'ab' * 'cd'
ok
Concatenation of two strings, and repetition of a string by an integer — in either order, since 3 * 'ab' is the same as 'ab' * 3. These are the only two string operations defined among the arithmetic operators.
err
Plus with a string and an int has no defined meaning, so Python refuses rather than guessing whether you meant to join or to add. Minus is not defined for strings at all. And star with two strings fails because neither operand can serve as the repetition count.

34. Translate: from the operator to what it means here

Translation

The same symbol, four different jobs, chosen by the types on either side.

Match the pairs

  • a. + with two ints
  • b. + with two strings
  • c. * with two ints
  • d. * with a string and an int
  • r1. addition: 3 + 4 gives 7
  • r2. concatenation: 'ab' + 'cd' gives 'abcd'
  • r3. multiplication: 3 * 4 gives 12
  • r4. repetition: 'ab' * 3 gives 'ababab'

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.

35. Comments: saying why, because the code already says what

Section

Section 4

36. How to write one, and what to put in it

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
LineWhat it isWhat Python does with it
line 1a comment on a line by itselfignored entirely
line 2the statement it describesruns normally
line 4a comment at the end of a linethe 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

37. Picture it: a redundant comment and a useful one

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

Both are the book's own examples. Only one earns its line.

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.

38. Worked example: improving a comment

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
VersionWhat the comment suppliesVerdict
the first commentrestates the operator and the numberadds nothing
the second commentsays what 1.2 MEANSadds the reason
the testcould 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 whole run at once: each drop is one line of the program.

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.

39. Eliminate: which comment is worth keeping?

Elimination

All four are accurate today. Only one is worth the line.

Eliminate the wrong options

The line is: wait = 3. Which comment survives?

  • A. # set wait to 3
  • B. # wait
  • C. # seconds to wait before retrying; the API rejects faster polling
  • D. # assignment statement

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?

40. Worked example: the tradeoff between names and comments

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
VersionWhere the meaning livesCost
t with a commentthe name says nothing; the comment carries it allcomment can drift from the name
elapsed_secondsthe name carries the meaningno comment needed
the very long namemeaning carried, readability lostexpressions 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 one is usually right, which is why naming came first in this chapter.

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.

41. Trap: the comment that used to be true

Trap

The 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.

The fix

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.

42. Predict: what does Python do with this line?

Prediction

The hash can appear anywhere on a line. Work out what that means.

print('a')  # print('b')
Part of the lineBefore or after the hashEffect
print('a')before the hash, so it runsdisplays a
# print('b')after the hash, so it is ignoreddisplays nothing
resultone line of outputa

Predict first

What does this line display?

  • a and b
  • just a
  • just b
  • nothing, the whole line is a comment

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.

43. Two truths and a lie: comments

Two truths and a lie

Two of these are true. Keep the lie.

Eliminate the wrong options

Rule out the two true statements.

  • A. Everything from the hash to the end of the line is ignored
  • B. A comment can appear at the end of a line of code, not only on its own line
  • C. A comment slows the program down slightly, since Python has to read past it

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.

44. Explain it yourself: why comment the why?

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.

45. Three kinds of error, and telling them apart

Section

Section 5

46. The taxonomy the rest of the course depends on

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

47. Picture it: where each kind of error stops you

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.

48. Worked example: three broken programs, one of each kind

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)
LineWhat happensWhich kind of error
line 1an unmatched closing parenthesissyntax error: nothing runs
line 2parses fine, fails when it runsruntime error: ZeroDivisionError
line 3runs fine, prints 212.0semantic 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

The whole run at once: each drop is one line of the program.

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.

49. Definition probe: which kind of error is this?

Definition probe

Diagnose from the symptom alone, without seeing the code.

Sort into buckets

Sort each symptom by the kind of error it indicates.

syntax error
Python prints a message and refuses to run the file; Python reports an unmatched parenthesis on line 12
runtime error
The program prints three lines and then stops with ZeroDivisionError; The program stops halfway with a NameError
semantic error
The program prints an average of 450 when every input was under 100; The program finishes and prints yesterday's date instead of today's
syn
The program never starts. If there is a syntax error anywhere in the file, Python displays a message and quits, so no part of the program runs — which is why the symptom is a total refusal rather than partial output.
run
The program started and produced some output before failing, which locates the error at a specific moment during execution. These are also called exceptions, because something exceptional happened.
sem
The program ran to completion with no message at all, and the output is wrong. No tool reported anything, because nothing is wrong with the program as a program — only with the match between it and what was intended.

50. Worked example: a semantic error you can actually see

Worked example

This program runs, prints a number, and is wrong. Find out how.

degrees = 360
radians = degrees / 2 * 3.14159
print(radians)
LineWhat happensResult
degrees = 360assignmentdegrees -> 360
degrees / 2 * 3.14159left to right: (360 / 2) * 3.14159565.4862
print(radians)displays it565.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.

51. Trap: assuming no error message means no error

Trap

The 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.

The fix

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.

52. Predict: which kind will you meet least, this early?

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?

  • Syntax errors, because the programs are short
  • Runtime errors, because the early programs do little that can fail while running
  • Semantic errors, because the programs are simple enough to check
  • They are all equally common

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.

53. Match each error kind to how you find it

Matching

The three kinds need three different techniques, and knowing which one to reach for is the payoff of the taxonomy.

Match the pairs

  • syn. syntax error
  • run. runtime error
  • sem. semantic error
  • r1. read the reported line and the one before it, checking pairs of brackets and quotes
  • r2. read the traceback to find which line failed, then ask what value it was given
  • r3. compare the output with something you knew independently, then work backward

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.

54. Where the taxonomy pays off

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.

55. Compare: the three kinds of error

Comparison

Fill the blanks from memory. This table is the most portable thing in the chapter.

Comparison matrix

KindWhen it appearsHow you find it
syntax errorbefore anything runs — Python quitsread the reported line and check bracket and quote pairs
runtime errorpartway through, with a messageread the traceback for the line, then ask what value it got
semantic errornever — the program finishes cleanlycompare 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.

56. The procedure: writing an expression you can trust

Pattern

Four sections of housekeeping, collapsed into one routine you can apply to any line you write.

  1. Write the expression the way you would say it out loud.
  2. Find every place where two operators meet, and ask which goes first.
  3. Wherever you cannot tell by looking, add parentheses — this is the book's own advice, and it costs nothing.
  4. Check the types: if any operand is a string, work out what the operator means for strings before assuming it means arithmetic.
  5. Add a comment saying WHY, if there is a why that the code cannot express — a unit, a rate, a reason.
  6. Test it on a value whose answer you already know, so that a semantic error has something to fail against.

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

57. Check yourself 1 of 3: precedence

Check

Two operators on different levels. Which goes first?

>>> 2 * 3 ** 2
18
Sub-expressionWhy it runs when it doesValue
3 ** 2exponentiation is higher than multiplication9
2 * 9then the multiplication18
resultnot 36which would need (2 * 3) ** 2

Check your understanding

Why is 2 * 3 ** 2 equal to 18 rather than 36?

  • A. Because multiplication and exponentiation are on the same level, so it goes left to right
  • B. Because exponentiation has higher precedence, so 3 ** 2 is computed first (correct)
  • C. Because exponentiation groups right to left
  • D. Because Python rounds the result down

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.

Why A tempts people
They are not on the same level. If they were, left to right would give (2 * 3) ** 2, which is 36 — the answer this option would predict and Python does not give.
Why C tempts people
Exponentiation does group right to left, but that rule only matters when there are two exponentiation operators. Here there is one, so the rule has nothing to do.
Why D tempts people
No rounding takes place. Both 18 and 36 are whole numbers, and Python computed the one the precedence rules select.

58. Check yourself 2 of 3: string operators

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?

  • A. 'ab' * 3
  • B. 'ab' + 'cd'
  • C. 'ab' + 3 (correct)
  • D. 3 * 'ab'

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.

Why A tempts people
This is repetition with an integer count, which is exactly the defined case. It gives 'ababab'.
Why B tempts people
Two strings and a plus is concatenation, the other defined string operation. It gives 'abcd'.
Why D tempts people
Repetition does not care which side the string is on, so this is the same as 'ab' * 3 and gives 'ababab'.

59. Check yourself 3 of 3: which kind of error?

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?

  • A. A syntax error
  • B. A runtime error
  • C. A semantic error (correct)
  • D. No error — Python computed what it was told

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.

Why A tempts people
A syntax error would have stopped the program before it ran at all. This one ran to completion, so the structure of the program is fine.
Why B tempts people
A runtime error would have stopped it partway with a message. This one finished cleanly, so nothing exceptional happened while it ran.
Why D tempts people
It is true that Python computed what it was told — the book says exactly that about semantic errors. But an answer of 60 for that average is still an error, because the program does not do what was intended, and that mismatch is what the word names.

60. Where this shows up outside this course

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.

61. Confidence wager: commit before you check

Commit first

Answer, then rate your confidence. This one has caught professionals.

Predict first

What is the value of 2 3 2?

  • 64
  • 512
  • 36
  • An error

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.

62. Explain it to someone else

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.

63. Exit ticket

Exit ticket

One honest answer. It decides what the next lesson opens with.

Predict first

Which of these is still least solid for you?

  • Precedence, and the left-to-right rule with its one exception
  • What plus and star do to strings, and which combinations are illegal
  • Writing a comment that is worth the line it takes
  • Telling the three kinds of error apart from the symptom

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.

64. Synthesis: draw the map of this lesson

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.

65. What you can do now

Recap

Two pages, and chapter 2 is finished: names, values, meaning and the ways it can go wrong.

If you remember one thingIt is this
From precedenceIf you cannot tell by looking, use parentheses. Do not rely on memory.
From left-to-rightdegrees / 2 * pi does not divide by two pi. It divides by two, then multiplies.
From stringsConcatenation is not commutative. Order is the whole content of a string.
From commentsComment the why. The what is already written directly below.
From the taxonomyNo 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

Sources

  1. Think Python, 2nd edition — Allen B. Downey — Allen B. Downey, Think Python: How to Think Like a Computer Scientist, 2nd edition (Green Tea Press, 2015), §2.5-2.8, pp. 11-13
  2. Python documentation — Expressions
  3. Python documentation — An Informal Introduction to Python

Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.

Book on Wyzant · Text (657) 465-8108