5a Floor Division, Modulus, and Boolean Expressions

This lesson introduces floor division and the modulus operator with the uses that make them worth knowing, then the relational and logical operators and the bool type, assembling everything an if statement needs for a condition.

Subject: Python · 65 slides · code lesson

Open the interactive version of this deck

What this lesson covers

The lesson, slide by slide

1. Lesson 5a Floor Division, Modulus, and Boolean Expressions

Title

Python · Chapter 5 — Conditionals and recursion

§5.1-5.3, pp. 39-40

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 §5.1-5.3, pp. 39-40 — the pages these objectives are drawn from

3. Before we start: how long is 105 minutes?

Warm-up

You know how to divide. The question is what you want the answer to look like.

Discussion prompt

A film runs for 105 minutes. Work out how many whole hours that is, and how many minutes are left over. Then write down what Python's ordinary division gives you for 105 divided by 60, and say why that is not quite the answer you wanted.

Hint: One hour and forty-five minutes. Python gives you something else.

Answer:

Python's division gives 1.75. That is a correct number and the wrong shape: nobody says a film is 1.75 hours long.

You wanted two numbers — one whole hour, and forty-five minutes left over — and ordinary division gives you one number with the two mixed together.

Python has an operator for each half of that answer, and this lesson starts with both. They are the operators that make working in whole numbers practical.

4. The one idea behind this lesson: conditions are values

Concept

A boolean expression is an expression that is either true or false. That sounds like a description of a question, and it is really a description of a VALUE — True and False are values with a type of their own, and a condition is just an expression that produces one.

boolean expression — An expression whose value is either True or False.

This matters because everything you already know about expressions applies. A boolean expression can be assigned to a variable, passed to a function, or combined with operators — and in the next lesson it will be the thing an if statement looks at.

Figure (svg): A diagram showing two values entering a relational operator and a True or False value coming out

The same two-in-one-out shape as any other operator. The output type is what is new.

Think Python, 2nd edition — Allen B. Downey §5.1-5.3, pp. 40-40

5. Floor division: whole numbers of things

Section

Section 1

6. The operator that rounds down

Concept

The floor division operator divides two numbers and rounds down to an integer. It is written as two slashes, and it exists because ordinary division does not give the answer you usually want when you are counting whole things.

>>> minutes = 105
>>> minutes / 60
1.75
>>> hours = minutes // 60
>>> hours
1
ExpressionWhich operatorResult
minutes / 60ordinary division1.75, a float
minutes // 60floor division1, an int
the differencethe fraction is dropped, not showna whole number of hours

We do not normally write hours with decimal points. Floor division returns the integer number of hours, rounding down — which is what you want when the thing being counted cannot be fractional.

Think Python, 2nd edition — Allen B. Downey §5.1-5.3, pp. 39-39

7. Picture it: two operators, two answers

Picture it

Both are correct. They answer different questions.

Figure (svg): Two columns contrasting ordinary division producing a float with floor division producing an int

One slash keeps the fraction. Two slashes throw it away.

This also answers a question left open in lesson 1b: the double-slash operator that produced an int from 84 and 2 was floor division, and now you know why it exists.

8. Worked example: hours and minutes

Worked example

Two operators, two halves of one answer.

>>> minutes = 105
>>> hours = minutes // 60
>>> remainder = minutes - hours * 60
>>> hours, remainder
(1, 45)
StepWhat it computesValue
minutes // 60how many whole hours fit1
hours * 60how many minutes those hours account for60
minutes - 60what is left over45

Get the whole hours with floor division.

Why: One whole hour fits into 105 minutes. The rounding-down is what makes this the count of COMPLETE hours rather than a fraction.

Work out what those hours used up.

Why: One hour is sixty minutes, so sixty of the 105 are accounted for.

Subtract to find the leftover.

Why: Forty-five minutes remain, which is the answer you wanted from the warm-up.

Figure (svg): The state of the program after each line of Worked example hours and minutes, drawn as a ladder with one rung per traced line

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

One hour and forty-five minutes. Floor division gave the whole part; a subtraction recovered the remainder.

Verify: Check that the two parts reconstruct the original.

Why: One hour is sixty minutes, plus forty-five, is 105 — the number you started with. Any split of a quantity into a whole part and a remainder must reassemble, and checking that catches an off-by-one immediately.

9. Predict: what does 17 // 5 give?

Prediction

Divide, then round down.

Predict first

What is the value of 17 // 5?

  • 3.4
  • 3
  • 4
  • 2

Correct: 3 — seventeen divided by five is 3.4, and floor division rounds down to 3.

Why: Three whole fives fit into seventeen, with two left over. Note that the result is an int rather than a float: floor division on two ints gives an int, which is the whole reason it exists as a separate operator from the single-slash division that always gives a float.

10. Worked example: floor division rounds DOWN, not toward zero

Worked example

For positive numbers these are the same. Find the case where they differ.

>>> 7 // 2
3
>>> -7 // 2
-4
>>> int(-7 / 2)
-3
ExpressionWhat happensResult
7 // 23.5 rounded down3
-7 // 2-3.5 rounded DOWN-4
int(-7 / 2)-3.5 chopped toward zero-3

Check the positive case.

Why: Seven divided by two is 3.5, and rounding down gives 3. Chopping toward zero would also give 3, so this case tells you nothing about which rule is operating.

Check the negative case.

Why: Minus seven over two is -3.5. Rounding DOWN means going to the more negative side, giving -4.

Compare with int().

Why: int chops toward zero, from lesson 3a, giving -3. So the two are genuinely different operations, and they differ exactly on negative numbers.

Figure (svg): A number line showing minus three point five with arrows to minus four for floor division and minus three for int

Floor division goes left to -4. int() goes toward zero, to -3.

Floor division rounds down — toward more negative — while int() chops toward zero. They agree on positive numbers and disagree on negatives.

Verify: Check that the remainder still reconstructs the original in the negative case.

Why: -4 times 2 is -8, and -8 plus 1 is -7, so the remainder is 1 rather than -1. The two operators are designed to agree with each other: floor division and modulus always satisfy the reconstruction, which is why they round the way they do.

11. Trap: assuming floor division and int() are the same

Trap

The trap

A student tests both on positive numbers, sees identical answers, and treats them as interchangeable.

Generalise from the cases that happen to agree

Why: On positive numbers they always agree, and most test data is positive.

The first negative value produces answers that differ by one, in a program that has been correct for months.

The fix

They round differently, and the difference only shows on negatives.

Floor division rounds DOWN, always

Why: Toward the more negative side of the number line, whatever the sign.

int() chops toward zero

Why: Which is upward for a negative number.

Choose deliberately. Counting complete units usually wants floor division, because it agrees with the modulus operator and the two reconstruct the original — which is a property worth having.

12. Discriminate: which operator do you want?

Discrimination

The question decides the operator.

Sort into buckets

For each question, is the answer given by / or by //?

floor division: //
How many whole boxes of 12 can I fill from 100 items?; How many complete weeks are there in 45 days?; How many full pages of 50 lines does a 373-line file need?
ordinary division: /
What is the average of these two numbers?; What fraction of an hour is 20 minutes?; What is 3 divided by 4, exactly?
floor
Each of these counts complete units of something that cannot be fractional — boxes, weeks, pages. A fractional answer would be meaningless, and rounding down gives the number that actually fits.
true
Each of these genuinely wants the fractional part. An average, a proportion and an exact quotient all lose their meaning if the fraction is discarded.

13. Complete it: seconds into minutes and seconds

Faded example

One floor division and one subtraction, exactly as with the film.

Fill in the blanks

total = 500
minutes = total // 60
seconds = total - minutes * 60

Why: Floor division gives the number of complete minutes, which is 8, and the subtraction recovers the 20 seconds left over. Using a single slash here would give 8.333..., and multiplying that back by 60 would give 500 exactly, leaving a remainder of zero — which is arithmetically true and useless, because you wanted the split rather than the original number back.

14. Think it through: why two division operators?

Socratic

Most languages have one. Python has two on purpose.

Discussion prompt

Python 2 used a single slash for both, choosing floor division when both operands were integers and ordinary division otherwise. Name one advantage of that design and one problem with it.

Hint: Think about a division where you do not know the types in advance.

Answer:

The advantage: one operator to learn, and integer arithmetic behaves like integer arithmetic without extra syntax.

The problem is the one lesson 1b's socratic probe predicted: the MEANING of the operator would depend on the types of its operands, so you could not tell by reading a line what it would do. A division of two variables might be floor division on Tuesday and ordinary division on Wednesday, depending on the data.

Python 3 split them so that each operator does one thing always. The book flags the difference explicitly, because it is the change most likely to bite somebody reading older code.

15. The modulus operator, and why it is more useful than it looks

Section

Section 2

16. The remainder, and two things it tells you

Concept

The modulus operator divides two numbers and returns the remainder. It is written with a percent sign, and it has nothing to do with percentages.

>>> minutes = 105
>>> remainder = minutes % 60
>>> remainder
45
ExpressionWhat it meansValue
105 % 60how much is left after taking out whole 60s45
compareminutes - (minutes // 60) * 60also 45
the pair// gives the whole part, % gives the resttogether they split a number

The modulus operator is more useful than it seems. You can check whether one number is divisible by another — if x % y is zero, then x is divisible by y. And you can extract the right-most digits: x % 10 gives the last digit of x, and x % 100 the last two.

Think Python, 2nd edition — Allen B. Downey §5.1-5.3, pp. 39-40

17. Picture it: floor division and modulus split a number in two

Picture it

The pair always reconstructs the original, which is what makes them a pair.

Figure (svg): A diagram showing 105 splitting into one whole hour via floor division and forty-five minutes via modulus

The two operators are two halves of one answer.

That reconstruction is a check you can run on any pair of results, and it is also the reason floor division rounds down rather than toward zero.

18. Worked example: testing divisibility

Worked example

The first of the two uses the book highlights. It becomes a condition in the next lesson.

>>> 12 % 3
0
>>> 13 % 3
1
>>> 100 % 10
0
ExpressionWhat the remainder meansConclusion
12 % 3nothing left over0, so 12 is divisible by 3
13 % 3one left over1, so 13 is not
100 % 10nothing left over0, so 100 is divisible by 10

State the rule.

Why: If x % y is zero, then x is divisible by y. A zero remainder means the division came out exactly.

Check a case that is not divisible.

Why: 13 % 3 is 1, so three does not divide thirteen. The remainder tells you not only THAT it fails but by how much.

Notice where this is heading.

Why: x % y == 0 is a boolean expression, and in the next lesson it will be the condition of an if statement — which is why divisibility is worth a section this early.

Figure (svg): The state of the program after each line of Worked example testing divisibility, drawn as a ladder with one rung per traced line

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

A remainder of zero means divisible. The test is written as x % y equal to zero, and it works for any pair of whole numbers.

Verify: Check the rule against a number you know is even.

Why: Any even number gives 0 for n % 2, and any odd number gives 1. Testing on a case where you already know the answer is how you confirm you have the operator the right way round — n % 2 rather than 2 % n, which gives something quite different.

19. Predict: what is 7 % 10?

Prediction

The dividend is smaller than the divisor. Reason it through.

Predict first

What is the value of 7 % 10?

  • 0
  • 7
  • 0.7
  • An error

Correct: 7 — no whole tens fit into seven, so all of it is left over.

Why: Floor division gives 7 // 10 = 0, meaning zero whole tens fit. The reconstruction rule then forces the remainder to be 7, since 0 times 10 plus 7 is 7. This is also consistent with the digit rule: the last digit of 7 is 7. People often expect 0 here by analogy with dividing a small number by a big one, which is what ordinary division would suggest.

20. Worked example: extracting the last digits

Worked example

The second use, and it is less obvious. Work out why it holds.

>>> 1234 % 10
4
>>> 1234 % 100
34
>>> 1234 // 10 % 10
3
ExpressionWhat it extractsResult
1234 % 10the last digit4
1234 % 100the last two digits34
1234 // 10 % 10drop the last digit, then take the new last3

See why % 10 gives the last digit.

Why: Dividing by ten takes out all the tens, hundreds and thousands exactly; whatever is left over is what was in the units column.

Extend it.

Why: Similarly x % 100 yields the last two digits, because dividing by a hundred takes out everything above the tens column.

Combine with floor division for the digit before last.

Why: 1234 // 10 is 123, discarding the last digit; taking that modulo 10 gives 3, which was the tens digit of the original.

Figure (svg): The digits of 1234 drawn as boxes with the last one highlighted as what modulo ten extracts

Modulus keeps the right-hand end; floor division keeps the left.

The remainder on division by ten is the last digit, by a hundred the last two. Combining floor division with modulus reaches any digit you like.

Verify: Reconstruct the number from its parts.

Why: 1234 // 10 is 123 and 1234 % 10 is 4, and 123 times 10 plus 4 is 1234. The same reconstruction check as before, now peeling one digit rather than one hour — which is a good sign that the two operators really are one idea applied at different scales.

21. Trap: reading the percent sign as a percentage

Trap

The trap

A student sees 105 % 60 and expects something to do with proportions — perhaps 105 percent of 60.

Import the symbol's everyday meaning

Why: The percent sign means exactly one thing outside programming, and it is not this.

The expectation produces confident wrong predictions: 63 rather than 45, with no error to correct it.

The fix

In Python the percent sign is the modulus operator and has nothing to do with percentages.

Read x % y as the remainder when x is divided by y

Why: Say it that way out loud until the symbol stops suggesting proportions.

Compute a percentage with arithmetic instead

Why: x * 100 / y, which uses no special operator at all.

This is the same class of mistake as the caret in lesson 1b: a symbol whose everyday meaning differs from its Python meaning, producing a confident wrong answer rather than an error.

22. Match each expression to what it extracts

Matching

Floor division and modulus, used together, reach any part of a number.

Match the pairs

  • a. n % 10
  • b. n // 10
  • c. n % 2
  • d. n % 100
  • r1. the last digit
  • r2. everything except the last digit
  • r3. 0 if n is even, 1 if n is odd
  • r4. the last two digits

Why: Modulus keeps the right-hand end of a number and floor division keeps the left, which is why they combine so well. The third one is the same idea with a divisor of two: the remainder on division by two is 0 or 1, and those are exactly the two cases of even and odd — which is why it becomes the standard evenness test in the next lesson.

23. Fill the middle: test whether a year is a leap year candidate

Fill the middle

The first part of the leap-year rule is a divisibility test.

Fill in the blanks

year = 2024
is_divisible_by_4 = year % 4 == 0

Why: The remainder on division by four is zero exactly when the year is divisible by four, so this expression is True for 2024 and False for 2023. Note that the whole line is a boolean expression assigned to a name — which is the point the big idea was making: a condition is a value, and it can be stored like any other. The full leap-year rule needs two more divisibility tests, and the next lesson has the syntax to combine them.

24. Push the boundary: what is x % 1?

Edge cases

A divisor of one is a legitimate edge case. Predict it, then explain it.

Discussion prompt

What is n % 1 for any whole number n? Explain your answer using the reconstruction rule, and then say what n % 1 gives when n is a float such as 3.75.

Hint: How many whole ones fit into n?

Answer:

For a whole number it is always 0. Exactly n whole ones fit into n, so nothing is left over, and the reconstruction n // 1 times 1 plus 0 gives n.

For a float it is the fractional part: 3.75 % 1 is 0.75. Three whole ones fit, leaving three quarters — the same rule, applied to a number that has a fraction to leave over.

That makes n % 1 a compact way of asking what is the fractional part of this number, which is a genuinely useful thing to know and follows from the definition rather than being a special case.

25. Boolean expressions and the relational operators

Section

Section 3

26. A new type, with exactly two values

Concept

A boolean expression is an expression that is either true or false. True and False are special values that belong to the type bool, and they are not strings.

>>> 5 == 5
True
>>> 5 == 6
False
>>> type(True)
<class 'bool'>
ExpressionWhyValue
5 == 5the two operands are equalTrue
5 == 6they are notFalse
type(True)a type of its ownbool

Although these operations are probably familiar, the Python symbols differ from the mathematical ones. There is no such thing as the reversed forms — the equals sign always comes second in the two-character operators.

Think Python, 2nd edition — Allen B. Downey §5.1-5.3, pp. 40-40

27. Picture it: a fourth type joins the three you know

Picture it

int, float, str — and now bool, with exactly two values in it.

Figure (svg): Two columns comparing the bool type with the three types met earlier, showing how many values each has

A type with two values is unusual, and it is exactly what a yes-or-no question needs.

True and False are written with capital letters and no quotation marks. 'True' with quotes is a string, and by lesson 1b's rule it is a completely different value.

28. Worked example: the six relational operators

Worked example

Predict each result before advancing. All six produce a bool.

>>> 5 == 5
True
>>> 5 != 5
False
>>> 5 > 3
True
>>> 5 <= 5
True
ExpressionWhat it asksValue
5 == 5equalTrue
5 != 5not equal — but they areFalse
5 > 3greater thanTrue
5 <= 5less than OR equalTrue

Read each as a question with a yes-or-no answer.

Why: Is five equal to five? Yes. Is five not equal to five? No.

Watch the two-character operators carefully.

Why: The equals sign comes second in all of them: !=, >=, <=. There is no such thing as =< or =>, and writing one is a syntax error.

Note the fourth case.

Why: Five is not less than five, but it is equal to it, and <= asks whether EITHER holds. That is why it is True.

Figure (svg): The state of the program after each line of Worked example the six relational operators, drawn as a ladder with one rung per traced line

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

True, False, True, True. Every relational operator produces a bool, whatever types it was comparing.

Verify: Check the complementary pairs.

Why: 5 == 5 and 5 != 5 give opposite answers, as do 5 > 3 and 5 <= 3. Each operator has an exact opposite, and checking that a pair disagrees is a quick way to confirm you have read the symbols correctly.

29. Predict: what does '5' == 5 give?

Prediction

Two values that look the same and are not.

Predict first

What is the value of '5' == 5?

  • True
  • False
  • A TypeError
  • A SyntaxError

Correct: False — a str and an int are never equal, however similar they look.

Why: Notice that this is not an error. Python is perfectly willing to compare values of different types with the equality operator; it simply answers no. That silence is what makes it dangerous — unlike '5' + 5, which raises a TypeError and stops you, this quietly gives False and lets the program continue with a condition that will never be true.

30. Worked example: the single-equals mistake

Worked example

The most common syntax error in early Python. Cause it deliberately.

if x = 5:
    print('five')
What was writtenWhat it isResult
x = 5an assignment, which is a statementstatements have no value
if needs a valueit got a statementSyntaxError
x == 5a comparison, which is an expressionwhat was meant

Recall the distinction from lesson 2a.

Why: A single equals sign is an assignment operator and produces no value. A double equals sign is a relational operator and produces a bool.

See why the if statement rejects it.

Why: An if statement needs a condition — an expression with a value. An assignment is a statement, and statements do not have values, so there is nothing for the if to test.

Write what was meant.

Why: if x == 5: compares, produces True or False, and the if statement has something to work with.

Figure (svg): Two columns contrasting the assignment operator with the equality operator, showing what each produces

One character apart, and on opposite sides of the expression-statement divide.

A SyntaxError. A common error is to use a single equal sign instead of a double equal sign — one assigns and the other compares, and only the second produces a value an if statement can use.

Verify: Check the diagnosis against the expression-versus-statement test from lesson 2a.

Why: The test was: could this be used where a value is expected? An assignment could not, which predicted this error before you ever saw an if statement. A rule from three lessons ago explaining a new error is a good sign it was the right rule.

31. Trap: comparing a number with a string that looks like one

Trap

The trap

A program reads '42' from the user and tests whether it equals 42.

Compare for the meaning rather than the value

Why: Both are obviously forty-two to a human reader.

The comparison gives False, with no error at all. A str and an int are never equal, whatever their contents look like, so the branch never runs and the program silently does the wrong thing.

The fix

Compare values of the same type, converting first if necessary.

Convert the text to a number at the boundary

Why: int(answer) == 42, using the conversion from lesson 3a.

Or compare as text if that is what you mean

Why: answer == '42' is also correct, and it is the right choice if you care about the exact characters typed.

This becomes urgent in the next lesson: input always returns a string, so every comparison with a number needs a conversion — and forgetting it produces a condition that is silently always False.

32. Sort: does this produce a bool?

Sorting

Relational operators produce bools. Assignments and arithmetic do not.

Sort into buckets

For each line, does it produce a boolean value?

produces True or False
x == 5; x > 5; x != 5
does not
x = 5; x + 5; x % 5
yes
Each uses a relational operator, whose job is to compare two operands and answer yes or no. The answer is a value of type bool.
no
One is an assignment, which is a statement and has no value at all. The other two are arithmetic, which produces numbers — and a number is not a bool, even when it happens to be 0 or 1.

33. Two truths and a lie: booleans

Two truths and a lie

Two are true. Keep the lie.

Eliminate the wrong options

Rule out the two true statements.

  • A. True and False belong to a type called bool
  • B. There is no such operator as =< in Python
  • C. True is the same as the string 'True'

Survives elimination: C

Why: C is the lie, and it is the same shape as the None-versus-'None' confusion from lesson 3c. True and False are special values that belong to the type bool; they are not strings. Quotation marks make a str, whatever is inside them, so 'True' is a four-character piece of text with a completely different type.

34. Decode the notation: reading a comparison

Notation

Six operators, and the two-character ones have a rule.

Annotate

  • All three are two characters, and in all three the equals sign comes SECOND. That is the rule, and it has no exceptions.
  • The exclamation mark means not, so != is not equal to. This is the only place in Python where an exclamation mark means negation.
  • The >= operator means greater than OR equal to, so it is True in two different situations. That is one more than beginners usually expect.
  • The same applies to <=, which is why 5 <= 5 is True even though 5 < 5 is False.
  • Writing =< or => is a syntax error rather than an alternative spelling. There is no such thing, and Python will not guess what you meant.
  • And none of these is the assignment operator. A single equals sign assigns; every operator here compares.

The equals-second rule is worth memorising deliberately, because the mistake is a syntax error that stops the program rather than a wrong answer — which makes it annoying rather than dangerous.

35. Logical operators: and, or, not

Section

Section 4

36. Combining conditions

Concept

There are three logical operators: and, or, and not. Their meaning is similar to their meaning in English, which makes them unusually easy to read — and slightly dangerous, because English is looser than Python.

>>> x = 5
>>> x > 0 and x < 10
True
>>> x % 2 == 0 or x % 3 == 0
False
>>> not (x > 10)
True
OperatorWhen it is TrueThis case
andTrue only if BOTH sides are true5 is above 0 and below 10
orTrue if EITHER side is true, or both5 is divisible by neither 2 nor 3
notreverses a boolean5 is not above 10, so this is True

The book's examples are worth keeping: x greater than 0 and x less than 10 is true only if x is in that range, and n modulo 2 equal to 0 or n modulo 3 equal to 0 is true if the number is divisible by 2 or by 3 or by both.

Think Python, 2nd edition — Allen B. Downey §5.1-5.3, pp. 40-40

37. Picture it: what each operator requires

Picture it

Two operators that combine, one that reverses.

Figure (svg): Two columns showing what and requires against what or requires, with not shown as a reversal

and is True once out of four; or is True three times out of four.

The English word or is often exclusive — tea or coffee usually means one, not both. Python's or is inclusive: True or True is True.

38. Worked example: a range test with and

Worked example

Two comparisons, joined. Work out why both are needed.

>>> x = 5
>>> x > 0 and x < 10
True
>>> x = 50
>>> x > 0 and x < 10
False
CaseThe two comparisonsResult
x = 55 > 0 is True; 5 < 10 is TrueTrue and True gives True
x = 5050 > 0 is True; 50 < 10 is FalseTrue and False gives False
the ruleand needs bothone failure is enough

Evaluate each side separately.

Why: Each is an ordinary relational expression producing a bool. The logical operator combines the two results, not the original numbers.

Apply the rule for and.

Why: It is true only if both sides are true. With 50, the first side holds and the second does not, so the whole thing is False.

Notice what a single test could not do.

Why: No single comparison expresses between 0 and 10. Two are needed, and the logical operator is how they are joined.

Figure (svg): The state of the program after each line of Worked example a range test with and, drawn as a ladder with one rung per traced line

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

True for 5 and False for 50. The and operator requires both conditions, so failing either one is enough to make the whole expression False.

Verify: Test a value that fails the other side.

Why: With x = -3 the first comparison fails and the second succeeds, and the result is again False. Testing both ways round confirms that and is genuinely symmetric in what it requires, rather than only checking the first condition.

39. Predict: is this True?

Prediction

Evaluate each side, then combine.

n = 9
n % 2 == 0 or n % 3 == 0
Sub-expressionValueResult
n % 29 divided by 2 leaves 11 == 0 is False
n % 39 divided by 3 leaves 00 == 0 is True
False or Trueor needs only oneTrue

Predict first

What is the value of this expression when n is 9?

  • True
  • False
  • 9
  • An error

Correct: True — nine is not divisible by two, but it is divisible by three, and or needs only one side to hold.

Why: This is the book's own example. The first comparison is False because 9 % 2 is 1; the second is True because 9 % 3 is 0. The or operator is true if either or both of the conditions is true, so one success is enough. Note how each half combines two ideas from this lesson: modulus for the divisibility test, and a relational operator to turn the remainder into a bool.

40. Worked example: the truthiness caveat

Worked example

The book raises this and then advises against relying on it. Both halves matter.

>>> 42 and True
True
>>> 0 and True
0
ExpressionWhat Python doesNote
42 and Trueany nonzero number counts as trueTrue
0 and Truezero counts as false0
the adviceuseful, subtle, avoidableavoid unless you know why

Notice that this works at all.

Why: Strictly speaking, the operands of the logical operators should be boolean expressions, but Python is not very strict. Any nonzero number is interpreted as True.

Notice the second result is not a bool.

Why: 0 and True gives 0, not False. The operators do not always return a bool, which is one of the subtleties the book warns about.

Take the advice.

Why: This flexibility can be useful, but there are some subtleties that might be confusing, and you might want to avoid it unless you know what you are doing.

Figure (svg): A panel listing which values count as false in Python and noting that everything else counts as true

Python accepts non-boolean operands, treating any nonzero number as true — and it may hand back one of the operands rather than True or False. It is worth recognising and not worth relying on this early.

Verify: Write the explicit version and compare.

Why: n != 0 and True is unambiguous, always produces a bool, and reads as what it means. Being able to write the explicit version is the real defence: the shortcut is only dangerous when it is the only form you know.

41. Trap: writing a range test the way you would say it

Trap

The trap

Meaning x is between 0 and 10, a student writes 0 < x and < 10, or x > 0 and < 10.

Carry over English's ellipsis

Why: In English the second x is understood and omitting it is natural.

Python has no such convention. The and operator joins two complete expressions, and < 10 on its own is not one, so this is a syntax error.

The fix

Each side of a logical operator must be a complete expression.

Write both comparisons in full

Why: x > 0 and x < 10. The x appears twice because there are two comparisons.

Or use Python's chained form, which does allow the ellipsis

Why: 0 < x < 10 is legal and means exactly the same thing — the next lesson introduces it.

The chained form exists precisely because the English reading is so natural. It is the one place Python bends toward how you would say it, and it is limited to comparisons.

42. Compare: and, or, not

Comparison

Fill the blanks from the definitions.

Comparison matrix

OperatorHow many operandsTrue when
andtwoboth are true
ortwoat least one is true
notoneits operand is false

Note that not takes only one operand. It is the only logical operator that reverses rather than combines, which is why it is written in front of its operand rather than between two.

43. Translate: English to Python

Translation

Each English phrase corresponds to one arrangement of operators.

Match the pairs

  • a. x is between 1 and 10
  • b. n is even
  • c. n is not even
  • d. n is divisible by 2 or by 5
  • r1. x > 1 and x < 10
  • r2. n % 2 == 0
  • r3. n % 2 != 0
  • r4. n % 2 == 0 or n % 5 == 0

Why: Two things are worth noticing. The third could equally be written not (n % 2 == 0), which is longer and says the same thing — the != operator already contains the negation. And the fourth needs the full comparison on both sides of or: writing n % 2 == 0 or 5 would be legal Python meaning something quite different, because 5 on its own counts as true.

44. Find the counterexample: is or the same as English *or*?

Counterexample

The book says the meaning is similar to English. Find where it is not.

Discussion prompt

Give an everyday sentence where or clearly excludes the both-case, and say what Python's or would answer in the corresponding situation.

Hint: Menus are full of them.

Answer:

Would you like tea or coffee? excludes both. Answering yes, both is not a normal response to that question.

Python's or is inclusive: True or True is True. If a program tested is it raining or is it cold on a day that was both, it would answer True, which is usually what you want.

Where the difference bites is a condition meant to be exclusive — exactly one of these — which needs to be written out rather than assumed: (a and not b) or (b and not a). Knowing that Python's or does not mean that saves a class of quiet bug.

45. Putting it together: building a condition worth testing

Section

Section 5

46. Everything in this lesson, assembled

Concept

You now have all the pieces an if statement needs: operators that produce numbers you can compare, operators that turn those comparisons into booleans, and operators that combine booleans. Building a condition means using all three layers in order.

year = 2024
by_4 = year % 4 == 0
by_100 = year % 100 == 0
by_400 = year % 400 == 0
is_leap = by_4 and (not by_100 or by_400)
ExpressionWhich layers it usesValue for 2024
year % 4 == 0modulus, then comparisonTrue
year % 100 == 0modulus, then comparisonFalse
year % 400 == 0modulus, then comparisonFalse
by_4 and (not by_100 or by_400)logical operators combine themTrue

Notice that the three intermediate results are stored in named variables. A condition assembled from named parts is far easier to check than the same logic written as one long expression, and the names document what each part means.

Think Python, 2nd edition — Allen B. Downey §5.1-5.3, pp. 39-40

47. Picture it: three layers, bottom to top

Picture it

Arithmetic produces numbers; comparisons produce booleans; logical operators combine them.

Figure (svg): A three-stage diagram showing arithmetic feeding comparisons feeding logical operators

Each layer consumes what the one below produces.

Getting the layers in the wrong order is the commonest way a condition goes wrong: writing year % 4 as a condition on its own uses only the first layer, and the truthiness rule then makes it mean year is not a multiple of four, which is backwards.

48. Worked example: building a condition in named steps

Worked example

Assemble it piece by piece, checking each part before combining.

>>> n = 12
>>> n % 2 == 0
True
>>> n % 3 == 0
True
>>> n % 2 == 0 and n % 3 == 0
True
Sub-conditionWhat it asksValue
n % 2 == 0is it even?True
n % 3 == 0is it a multiple of 3?True
both togetherand requires bothTrue — so n is a multiple of 6

Check each part alone first.

Why: At the prompt this costs nothing and it localises any mistake to one comparison rather than to a compound expression.

Combine only once the parts are right.

Why: The and operator works on the two boolean results, so if either part was wrong the combination cannot be right.

Notice what the combination means.

Why: Divisible by 2 and by 3 is exactly divisible by 6, which is a sanity check you can apply independently.

Figure (svg): The state of the program after each line of Worked example building a condition in named steps, drawn as a ladder with one rung per traced line

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

True, and it means n is a multiple of six. Building the condition in parts made each step checkable at the prompt.

Verify: Test a number that satisfies only one part.

Why: With n = 9 the first part is False and the whole condition is False; with n = 8 the second part fails and it is again False. Checking both single-failure cases confirms the and is doing its job rather than accidentally reporting one side.

49. Predict: what does this expression give?

Prediction

Three layers. Evaluate them in order.

n = 15
n % 3 == 0 and n % 5 == 0
Sub-expressionValueResult
n % 315 divided by 3 leaves 00 == 0 is True
n % 515 divided by 5 leaves 00 == 0 is True
True and Trueand needs bothTrue

Predict first

What is the value of this expression?

  • True
  • False
  • 0
  • 15

Correct: True — fifteen is divisible by three and by five, so both comparisons hold.

Why: Both remainders are zero, so both comparisons produce True, and the and operator gives True. This condition is a test for divisibility by fifteen, which is a useful independent check: 15 % 15 is indeed 0. Building a compound condition and then verifying it against a simpler equivalent is a good habit whenever the logic is not obvious.

50. Worked example: a condition that is silently backwards

Worked example

This runs, produces a value, and means the opposite of what it says.

>>> n = 7
>>> n % 2
1
>>> n % 2 == 0
False
ExpressionWhat it producesWhat it means as a condition
n % 2the remainder, a number1
as a condition1 counts as truetrue for ODD numbers
n % 2 == 0the comparisonFalse for odd numbers — correct

Look at what the bare modulus produces.

Why: A number: 1 for odd, 0 for even. Not a boolean at all.

Apply the truthiness rule.

Why: Any nonzero number is interpreted as True, so n % 2 is true precisely when n is ODD — the opposite of what somebody writing an evenness test intends.

Add the comparison.

Why: n % 2 == 0 turns the number into a boolean that means what it says: True for even numbers.

Figure (svg): Two columns comparing the bare modulus used as a condition with the explicit comparison, showing they are opposites

One character apart in intent, exactly opposite in effect.

The bare modulus is true for odd numbers, because a remainder of 1 counts as true. The comparison with zero is what makes it an evenness test, and omitting it silently inverts the meaning.

Verify: Test both an even and an odd number against both forms.

Why: For 8 the bare form gives 0 (false) and the comparison gives True; for 7 they are 1 (true) and False. The two forms disagree on every input, which is as clear a demonstration as possible that the comparison is not optional.

51. Trap: leaving out the comparison because it *works*

Trap

The trap

A student writes a condition as n % 2 and finds it behaves correctly for their test — which happened to be an odd number.

Test one case and generalise

Why: The single test passed, and the expression is shorter.

Every even number now takes the wrong branch, silently. This is a semantic error in the sense of lesson 2b: no message, plausible behaviour, wrong result.

The fix

Write the comparison explicitly, so the condition says what it means.

Turn every number into a boolean with a relational operator

Why: n % 2 == 0 for even, n % 2 != 0 for odd. Both read as what they test.

Test both cases whenever a condition has two outcomes

Why: One even number and one odd number would have caught this immediately.

The book's advice about truthiness applies exactly here: the flexibility can be useful, but you might want to avoid it unless you know what you are doing — and a condition that means the opposite of its author's intention is what not knowing costs.

52. Error analysis: four attempted conditions

Error analysis

One is correct. Mark what is wrong with the other three.

Annotate

  • Line 1 uses a single equals sign, which is assignment. An assignment is a statement with no value, so there is nothing for the if to test — a SyntaxError.
  • Line 2 omits the second x. Each side of and must be a complete expression, and < 10 is not one. Also a SyntaxError.
  • Line 3 is legal and almost certainly wrong. It uses the bare remainder as a condition, which is true for ODD numbers, so an evenness test written this way is inverted.
  • The difference between lines 3 and 4 is the dangerous one: line 3 produces no error and behaves backwards, while lines 1 and 2 stop the program immediately.
  • Line 4 is correct: modulus produces a number, the comparison turns it into a boolean, and the boolean means what it says.
  • Two syntax errors and one semantic error, which is the taxonomy from lesson 2b showing up in four consecutive lines.

The two that stop the program are the harmless ones. The one that runs is the one to worry about.

53. Complete it: is this a single-digit positive number?

Faded example

Two conditions, joined. Write both comparisons in full.

Fill in the blanks

is_single_digit = x > 0 and x < 10

Why: Both conditions must hold for the number to be a positive single digit, so the operator is and. Using or would be True for any positive number at all and for anything below ten including negatives, which is a much weaker claim. Note that both comparisons are written out in full, each with its own x — the shorthand you would use in English is not available here, though the next lesson introduces a chained form that is.

54. Explain it: why does a condition need == and not =?

Explain it

This is the error every beginner hits, and the explanation is short if you have lesson 2a.

Discussion prompt

A classmate keeps writing if x = 5 and getting a syntax error. Explain in two sentences why Python rejects it, using the words expression and statement, and then give them a way to remember which is which.

Hint: One of the two produces a value and one does not.

Answer:

Say: a single equals sign is an assignment, which is a STATEMENT and has no value, and an if statement needs an EXPRESSION with a value to test. A double equals sign is a comparison, which produces True or False.

The mnemonic that works for most people: one equals sign does one thing, which is putting a value somewhere. Two equals signs ask a question about two things.

It is worth adding that this error is a gift. Some languages allow assignment inside a condition, where it silently assigns and then tests the assigned value — a bug that is very hard to see. Python's refusal turns a lurking semantic error into an immediate syntax error.

55. Compare: the three families of operator

Comparison

Fill the blanks. Each family takes different inputs and produces a different kind of output.

Comparison matrix

FamilyTakesProduces
arithmetic: // and %numbersnumbers
relational: == != < > <= >=two values of comparable typesa bool
logical: and, or, notbooleansa bool

The middle row is the bridge. It is the only family that takes numbers and produces booleans, which is why every condition has one in it somewhere.

56. The procedure: writing a condition you can trust

Pattern

Five steps, and the middle one is the one that gets skipped.

  1. Say in English what has to be true.
  2. Break it into simple claims, each about one thing — divisible by three, greater than zero, equal to this word.
  3. Write each claim as a comparison, so that it produces a bool rather than a number.
  4. Join the claims with and, or and not, using parentheses wherever the grouping is not obvious.
  5. Test each simple claim at the prompt on a case where you know the answer, then test the whole condition on one case that should be True and one that should be False.

Step 3 is the one people skip, and skipping it is how a bare remainder ends up as a condition meaning the opposite of what was intended. A condition should always contain a relational operator.

Python documentation — Built-in Types Built-in Types

57. Check yourself 1 of 3: modulus

Check

The remainder, not a percentage.

>>> 23 % 5
3
StepWhat happensValue
23 // 5four whole fives fit4
4 * 5those account for twenty20
23 - 20what is left3

Check your understanding

What is the value of 23 % 5?

  • A. 4
  • B. 3 (correct)
  • C. 4.6
  • D. 115

Answer: B

Why: Four whole fives fit into twenty-three, accounting for twenty, and three are left over. The modulus operator returns that remainder. The reconstruction check confirms it: 4 times 5 plus 3 is 23.

Why A tempts people
This is 23 // 5, the floor division — the number of whole fives rather than what is left over. The two operators answer the two halves of the same question.
Why C tempts people
This is 23 / 5, the ordinary division. Modulus never produces a fraction when both operands are whole numbers.
Why D tempts people
This reads the percent sign as a percentage. In Python it is the modulus operator and has nothing to do with proportions.

58. Check yourself 2 of 3: relational operators

Check

One of these is not an operator at all.

Check your understanding

Which of these is NOT valid Python?

  • A. x != y
  • B. x >= y
  • C. x =< y (correct)
  • D. x == y

Answer: C

Why: There is no such thing as =< or =>. In every two-character relational operator the equals sign comes second, so it is <= and >=. Writing it the other way round is a syntax error rather than an alternative spelling.

Why A tempts people
Valid: not equal to. The exclamation mark means not, and the equals sign follows it as the rule requires.
Why B tempts people
Valid: greater than or equal to. True in two situations, which is one more than beginners often expect.
Why D tempts people
Valid: the equality comparison, and the operator most often confused with the single-equals assignment.

59. Check yourself 3 of 3: logical operators

Check

Evaluate each side, then apply the rule for the operator.

x = 4
x > 0 and x > 10
Sub-expressionWhyValue
x > 04 is greater than 0True
x > 104 is not greater than 10False
True and Falseand needs bothFalse

Check your understanding

What is the value of this expression?

  • A. True
  • B. False (correct)
  • C. 4
  • D. An error, because x cannot be compared twice

Answer: B

Why: The first comparison is True and the second is False. The and operator is true only if both sides are true, so one failure is enough to make the whole expression False. Note that both comparisons are written out in full, each mentioning x — which is what the and operator requires.

Why A tempts people
This would be the answer for or, which needs only one side to hold. The two operators differ exactly in this case.
Why C tempts people
The expression produces a bool, not a number. The truthiness rule works the other way — numbers can be used where booleans are expected, not the reverse.
Why D tempts people
Comparing the same variable twice is entirely ordinary. It is how every range test is written, and each comparison is independent of the other.

60. Where this shows up outside this course

Real world

Remainders and compound conditions are everywhere once you look.

Discussion prompt

Find two places outside programming where a remainder is the useful part of a division rather than the quotient — and one place where a rule is stated as a compound condition with and, or or not. What would go wrong if the or in your example were read as exclusive?

Hint: Clocks, calendars and packing are all remainder problems.

Answer:

Clocks are the classic: what time is it 50 hours from now is a question about 50 modulo 24. Packing is another: how many items are left over after filling whole boxes.

Compound conditions are everywhere in rules and eligibility criteria. You may enter if you are a member or have a ticket is an inclusive or — being both does not disqualify you, and reading it exclusively would be absurd.

That is why Python's or is inclusive: it matches how rules are actually written, even though conversational English often means the exclusive version. When a rule really does mean exactly one, it says so.

61. Confidence wager: commit before you check

Commit first

Answer, then rate your confidence. This one catches people who have only tested positives.

Predict first

What is the value of -7 // 2?

  • -3
  • -4
  • -3.5
  • 3

Correct: -4 — floor division rounds DOWN, which for a negative number means away from zero.

Why: Minus seven over two is -3.5, and rounding down goes to the more negative side, giving -4. The tempting answer is -3, which is what int(-7 / 2) gives, because int chops toward zero. The two operations agree on every positive number and disagree on every negative one, which is why testing only positives leaves this unlearned. The reason floor division rounds this way is consistency with modulus: -4 times 2 is -8, and -8 plus 1 is -7, so the remainder is a clean 1 rather than a negative number.

62. Explain it to someone else

Explain it

The modulus operator is the one worth being able to motivate.

Discussion prompt

A classmate says the modulus operator seems pointless — why would anyone want a remainder? Give them two concrete uses in under a minute, and make one of them something they would actually write.

Hint: The book gives two, and one of them is about to become very common.

Answer:

First: divisibility. If x % y is zero then x is divisible by y, which is how you test whether a number is even, whether a year is a leap year, or whether something divides evenly into groups.

Second: digits. x % 10 gives the last digit and x % 100 the last two, which is how you take a number apart without converting it to text.

The one they will actually write is the evenness test, n % 2 == 0. It appears in the next lesson as the standard example of a condition, and it is probably the single most common use of the operator in beginner code.

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?

  • Floor division and modulus, and what each returns
  • The uses of modulus: divisibility and extracting digits
  • The relational operators, and telling = from ==
  • Combining conditions with and, or and not

Correct: Whichever you picked is the right answer — this one is for you, not for a mark.

Why: All four are prerequisites for the next lesson rather than ends in themselves. The two operators become routine within a few programs, though the negative-number behaviour of floor division is worth one deliberate experiment. The modulus uses are worth over-learning because they turn up constantly. The single-equals mistake stops being a problem quickly, since it produces an immediate syntax error. And combining conditions is the one that keeps mattering — the next lesson's chained conditionals are largely about when to use and rather than a chain of ifs.

64. Synthesis: draw the map of this lesson

Connect it up

One page, from memory.

Draw it

Draw three horizontal bands, one for each family of operator: arithmetic, relational, logical. Put the operators from this lesson in the right band, and draw arrows from each band to the one above showing what feeds what. Then, below the diagram, write down the one expression from this lesson that produces a number where a boolean was intended, and say in one sentence what it means and what it should have been.

65. What you can do now

Recap

Two pages, and every ingredient the if statement needs.

If you remember one thingIt is this
From floor divisionIt rounds DOWN. On negatives that is not the same as chopping.
From modulusA remainder of zero means divisible. That test is everywhere.
From relational operators= assigns, == compares. There is no =<.
From logical operatorsand needs both; or needs one; both sides must be complete expressions.
From truthinessA bare number as a condition is legal and usually means the opposite of what you wanted.

The next lesson is what all of this was for: the if statement, its else and elif forms, and how to keep a chain of conditions readable rather than nesting it into unreadability.

Think Python, 2nd edition — Allen B. Downey §5.1-5.3, pp. 39-40 — 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), §5.1-5.3, pp. 39-40
  2. Python documentation — Built-in Types
  3. Python documentation — Expressions

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

Book on Wyzant · Text (657) 465-8108