This lesson introduces the turtle module and the idea of a method, gives the for statement in its simplest form as a way of repeating instructions, and performs the first process move of the case study: wrapping working code in a function.
Subject: Python · 65 slides · code lesson
Open the interactive version of this deck
Title
Python · Chapter 4 — Case study: interface design
§4.1-4.4, pp. 29-32
Objectives
Five things, each one you can check yourself at an interpreter prompt.
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 29-32 — the pages these objectives are drawn from
Warm-up
You are about to draw a square with four straight lines and four turns. Think about how you would write it.
Discussion prompt
Using only what you know so far, write down the instructions for drawing a square: go forward, turn left ninety degrees, and so on. How many lines does it take, and what do you notice about them?
Hint: Count how many distinct instructions there really are.
Answer:
Seven lines: forward, turn, forward, turn, forward, turn, forward. Or eight if you turn at the end as well.
What you notice is that there are only TWO distinct instructions. The other five lines are repetitions of them.
That observation is the whole of this lesson's middle section. Writing the same two lines four times works perfectly and is the wrong shape, and the for statement is the right one.
Concept
This chapter presents a case study that demonstrates a process for designing functions that work together. The point is not the drawings — it is that you start with something small that works and improve it in named, repeatable steps.
encapsulation — Wrapping a piece of working code up in a function definition.
Four such steps are named in this chapter: encapsulation, generalization, interface design and refactoring. This lesson covers the first; the next lesson covers the other three and assembles them into a development plan.
Figure (svg): A four-stage diagram naming the case study's process moves in order: working code, encapsulation, generalization, refactoring
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 29-29
Section
Section 1
Concept
The turtle module, with a lowercase t, provides a function called Turtle, with an uppercase T, that creates a Turtle object. Once you have one, you move it by calling methods on it.
method — A function that is associated with an object and called using dot notation.
import turtle
bob = turtle.Turtle()
print(bob)
turtle.mainloop()| Line | What happens | Result |
|---|---|---|
| import turtle | makes the module available | the name turtle exists |
| turtle.Turtle() | a FUNCTION in the module, called | a new Turtle object |
| bob = ... | the object is named | bob refers to it |
| print(bob) | displays what it is | <turtle.Turtle object at 0x...> |
Printing bob displays something like a Turtle object at an address, which means bob refers to an object with type Turtle as defined in module turtle. mainloop tells the window to wait for the user to do something.
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 29-30
Picture it
Both lines use a dot. They are reaching into two different kinds of thing.
Figure (svg): Two columns contrasting the dot used on a module with the dot used on an object
That is why lesson 3a introduced the dot as look inside the thing on the left for the name on the right. That description covers both cases without needing to know what an object is.
Worked example
Four lines, and only one of them draws anything.
import turtle
bob = turtle.Turtle()
bob.fd(100)
turtle.mainloop()| Line | What happens | Effect on the drawing |
|---|---|---|
| import turtle | the module becomes available | nothing drawn |
| bob = turtle.Turtle() | a window appears with an arrow in it | the turtle exists |
| bob.fd(100) | the turtle moves forward 100 pixels | a line is drawn |
| turtle.mainloop() | the window waits | nothing further |
Import, then create.
Why: Turtle with a capital T is a function inside the module, and calling it creates the object. Both parts of turtle.Turtle() are doing work.
Call a method on the object.
Why: The method fd is associated with the turtle object called bob. Calling a method is like making a request: you are asking bob to move forward.
Understand mainloop.
Why: It tells the window to wait for the user to do something — which, here, is only closing it. Without it the window would vanish immediately.
Figure (svg): A turtle path showing a single horizontal line drawn one hundred pixels east from the starting point
One line segment, 100 pixels long, drawn eastward from the middle of the window. The argument of fd is a distance in pixels, so the actual size depends on your display.
Verify: Comment out the fd line and run it again.
Why: The window still appears, with the arrow in it, and nothing is drawn. That isolates fd as the line that draws, and confirms the other three are setup — a useful habit when a graphical program does the wrong thing.
Matching
Six methods, and two different kinds of argument between them.
Match the pairs
Why: The four movement methods split into two pairs by what their argument means: fd and bk take a distance in pixels, lt and rt take an angle in degrees. Passing 90 to fd and 90 to lt asks for completely different things, and nothing in the syntax warns you — which is the first hint of why lesson 4b cares so much about documenting what a parameter means.
Worked example
Three method calls. Predict the shape before advancing.
bob.fd(100)
bob.lt(90)
bob.fd(100)| Call | What it asks the turtle to do | What appears |
|---|---|---|
| bob.fd(100) | move forward 100 pixels | a line eastward |
| bob.lt(90) | turn left 90 degrees | no line — turning does not draw |
| bob.fd(100) | move forward again | a line northward |
Notice that fd draws and lt does not.
Why: Moving leaves a trail; turning changes direction in place. Two of the three lines produce visible line segments.
Track the direction.
Why: The turtle starts facing east. After turning left 90 degrees it faces north, so the second fd goes upward.
Read the argument units.
Why: The argument for lt and rt is an angle in degrees, and the argument for fd and bk is a distance in pixels. Two methods, two different kinds of number.
Figure (svg): The state of the program after each line of Worked example drawing a right angle, drawn as a ladder with one rung per traced line
Bob moves east and then north, leaving two line segments behind — a right angle opening up and to the right of the starting point.
Verify: Replace lt with rt and predict before running.
Why: The second segment goes south instead of north, mirroring the shape. Being able to predict a change in the drawing before running it is the whole skill this case study builds, and it is much cheaper than trial and error.
Trap
A student writes bob = turtle.turtle() with a lowercase t, or import Turtle with a capital.
Treat the capital letter as a typographical detail
Why: In English, capitalisation rarely changes meaning within a word.
In Python these are two different names. The module is turtle with a lowercase t; the function inside it that creates an object is Turtle with an uppercase T, and neither will answer to the other's name.
Read the capitalisation as information about what kind of thing each name is.
Lowercase for the module — the file of related functions
Why: import turtle brings in the module.
Uppercase for the thing that makes objects
Why: turtle.Turtle() is the function inside it that creates a Turtle.
This convention is used throughout Python and becomes meaningful in chapter 15, where you define such things yourself. For now, treat a capital first letter as a signal that the name creates objects.
Prediction
It is not an error and it is not blank. Reason about what bob refers to.
Predict first
What does print(bob) display, after bob = turtle.Turtle()?
Correct: Something like a turtle.Turtle object at a hexadecimal address.
Why: It means bob refers to an object with type Turtle as defined in module turtle. This is the same shape of display you saw for a function object in lesson 3b and for a module in 3a — Python's default way of showing something that has no better textual form. The hexadecimal number is where the object lives in memory, and it will differ every time you run the program, which is why it is never worth reading.
Discrimination
Only some turtle calls leave a trail.
Sort into buckets
For each call, does a line appear?
Socratic
fd(100) on its own is an error. Work out why the object is required.
Discussion prompt
Why can you not simply write fd(100)? What information would be missing, and what does bob supply that a plain function call could not?
Hint: Imagine a program with two turtles.
Answer:
It would be missing WHICH turtle to move. A plain function would have to be told, so you would end up writing fd(bob, 100) — which is nearly what a method call is.
With two turtles, bob and alice, the difference becomes essential: bob.fd(100) and alice.fd(100) move different turtles, and there is no ambiguity about which.
So a method is a function that already knows which object it is acting on, and the dot is how you say which. That is the whole idea, and chapter 17 shows that it is implemented almost exactly as the fd(bob, 100) version suggests.
Section
Section 2
Concept
The square you wrote by hand repeats two instructions four times. A for statement does the same thing more concisely, and its syntax is the shape you already know from a function definition.
loop — A part of a program that can run repeatedly.
for i in range(4):
print('Hello!')| Pass | What happens | Output |
|---|---|---|
| 1st time round | the body runs | Hello! |
| 2nd time round | the body runs again | Hello! |
| 3rd time round | and again | Hello! |
| 4th time round | and again, then the loop ends | Hello! |
The syntax of a for statement is similar to a function definition: a header that ends with a colon, and an indented body which can contain any number of statements. It is called a loop because the flow of execution runs through the body and then loops back to the top.
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 30-31
Picture it
This is the first time the flow of execution goes anywhere but forward.
Figure (svg): A flow chart showing the loop header, the body, and an arrow looping back to the header, with an exit once four passes are done
Compare this with lesson 3b's detour picture. A call goes out and comes back once; a loop comes back to the same place repeatedly, which is a genuinely different shape.
Worked example
Two lines in the body, four passes. Predict the shape and the number of turns.
for i in range(4):
bob.fd(100)
bob.lt(90)| Pass | What runs | State after |
|---|---|---|
| pass 1 | forward, then turn | east side drawn, now facing north |
| pass 2 | forward, then turn | north side drawn, now facing west |
| pass 3 | forward, then turn | west side drawn, now facing south |
| pass 4 | forward, then turn | south side drawn, now facing east again |
Count the statements in the body.
Why: Two, both indented under the header. The body can contain any number of statements, and the indentation is what says which ones.
Count the passes.
Why: Four, because of range(4). The body runs four times in total.
Track the direction at the end.
Why: After four left turns of 90 degrees the turtle is facing its original direction again, and it is back where it started.
Figure (svg): A turtle path showing a complete square drawn with four forward moves and four left turns
A complete square, drawn in four sides with four turns — and the turtle ends up back in its starting position, facing its starting direction.
Verify: Compare the loop version with the seven-line version from the warm-up.
Why: The seven-line version draws the same square but makes only three turns, so the turtle ends facing north rather than east. The drawings are identical and the final STATE is not, which is exactly the difference the next section is about.
Prediction
Count the passes and the statements in the body.
for i in range(3):
print('a')
print('b')| Pass | What runs | Output |
|---|---|---|
| pass 1 | prints a, then b | 2 lines |
| pass 2 | prints a, then b | 2 more |
| pass 3 | prints a, then b | 2 more |
Predict first
How many lines does this print?
Correct: Six — two statements in the body, three passes.
Why: The body runs three times, and each pass runs both of its statements. Multiplying the number of passes by the number of printing statements in the body gives the total. This is the same counting argument as lesson 1a's comparison of print statements with lines of output, and it is the first time in the course where the two counts differ because of repetition rather than because of a function call.
Worked example
The loop turns four times; the hand-written version turned three. Decide which is better.
# hand-written: 4 moves, 3 turns
bob.fd(100); bob.lt(90)
bob.fd(100); bob.lt(90)
bob.fd(100); bob.lt(90)
bob.fd(100)| Version | Turns and final direction | Verdict |
|---|---|---|
| hand-written | 3 turns, ends facing north | asymmetric code |
| the loop | 4 turns, ends facing east | every pass identical |
| cost of the extra turn | a little time, no visible change | worth it |
Notice what the extra turn costs.
Why: A little time, and nothing visible: turning draws nothing, so the two squares look identical.
Notice what it buys.
Why: It simplifies the code, because we do the same thing every time through the loop. A loop whose last pass is special cannot be written as a plain loop at all.
Notice the second benefit.
Why: This version leaves the turtle back in its starting position, facing the starting direction — which means you can draw another shape afterwards without first working out where the turtle ended up.
Figure (svg): The state of the program after each line of Worked example the extra turn, and why it is there, drawn as a ladder with one rung per traced line
The loop version makes one extra turn. It costs a negligible amount of time and buys two things: every pass is identical, and the turtle finishes where it started, facing the same way.
Verify: Draw two squares in a row with each version and compare.
Why: With the loop version, running it twice draws the same square twice. With the hand-written version, the second square starts facing north and comes out rotated. Leaving the turtle in a predictable state is what makes the function composable — which is exactly what encapsulation is about to require.
Trap
A student writes the loop with the turn un-indented, so that it sits outside the body.
Treat indentation as formatting
Why: In most prose and in many languages the amount of leading whitespace carries no meaning.
The result draws four overlapping lines in the same direction and then makes one turn. It is a legal program producing a completely different picture, with no error message.
The indentation IS the body, exactly as it is for a function definition.
Indent every statement that should repeat
Why: Two indented lines means two statements repeat, four times each.
Un-indent the statements that should run once, after the loop
Why: This is a real technique, not only a mistake — sometimes you want a final action after the repetition.
This is a semantic error in the sense of lesson 2b: it runs, it produces no message, and the picture is wrong. The only defence is predicting the drawing before you run it.
Invariant
Step through the square loop and track position and heading together.
Step through it
After which pass is the square first visibly complete — and why does the loop keep going after that?
The square is complete at the end of pass 4's fd, before its lt. The final turn is the extra one: it changes nothing visible and restores the heading, which is what makes the loop's passes identical to each other.
Faded example
The exterior angles of a regular polygon must sum to 360 degrees.
Fill in the blanks
for i in range(3):
bob.fd(100)
bob.lt(120)
Why: Three sides means three passes, and three equal turns that together bring the turtle back to its starting heading — so each turn is 360 divided by 3, which is 120. Note that the turn is the EXTERIOR angle, not the interior 60 degrees of an equilateral triangle: the turtle turns through the change in direction, not through the corner of the shape. That distinction catches almost everybody once.
Analogy
Both change the flow of execution. Match each to the shape it makes.
Match the pairs
Why: The important difference is where the flow goes at the end of the body. A function body ends by returning to the line after the call — a place it has never been. A loop body ends by returning to the top of the loop — a place it has just been. That is why a loop can repeat and a call cannot, and it is also why a function that calls itself, in chapter 5, blurs the distinction interestingly.
Section
Section 3
Concept
The first exercise asks you to put your square-drawing code into a function definition and then call the function, passing the turtle as a parameter. Wrapping a piece of code up in a function is called encapsulation.
def square(t):
for i in range(4):
t.fd(100)
t.lt(90)
square(bob)| Line | How far it is indented | What that means |
|---|---|---|
| def square(t): | the function's header | one parameter, t |
| for i in range(4): | indented once: inside the function | the loop header |
| t.fd(100) | indented twice: inside the loop | runs 4 times |
| square(bob) | flush left: outside both | the call |
The innermost statements are indented twice to show that they are inside the for loop, which is inside the function definition. The next line, square(bob), is flush with the left margin, which indicates the end of both the loop and the definition.
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 32-32
Picture it
The amount of leading whitespace is the only thing saying what is inside what.
Figure (svg): Two columns showing the three indentation levels of the encapsulated square function and what each level means
This is the first program in the book with two levels of nesting, and it is worth reading the indentation deliberately rather than by eye until it becomes automatic.
Worked example
Start from working code and wrap it. Nothing about the drawing changes.
# before: working code at the top level
for i in range(4):
bob.fd(100)
bob.lt(90)
# after: the same code, encapsulated
def square(t):
for i in range(4):
t.fd(100)
t.lt(90)
square(bob)| Step | What you do | Result |
|---|---|---|
| step 1 | indent the working code by four spaces | it is now a body |
| step 2 | add a header above it | def square(t): |
| step 3 | replace bob with the parameter name | t.fd, t.lt |
| step 4 | add a call below | square(bob) |
Start with code that already works.
Why: This is the first rule of the development plan: get something working before you reorganise it. Encapsulating broken code gives you a broken function.
Indent it and put a header on top.
Why: The loop and its body move one level right, so the innermost statements end up indented twice.
Replace the specific turtle with the parameter.
Why: Inside the function, t refers to the same turtle bob, so t.lt(90) has the same effect as bob.lt(90) did.
Add the call.
Why: Without it, the definition runs and draws nothing — which is lesson 3b's point about definitions producing no output.
Figure (svg): The state of the program after each line of Worked example encapsulating, step by step, drawn as a ladder with one rung per traced line
The same square, drawn by the same statements, now with a name. Nothing about the picture changed; what changed is that the code can be invoked, moved and reused.
Verify: Run it and compare the drawing with the un-encapsulated version.
Why: Identical. Encapsulation is supposed to change nothing about behaviour, so a different picture would mean a mistake was made during the move — usually a missed bob that should have become t.
Prediction
The definition is correct. Look at what follows it.
def square(t):
for i in range(4):
t.fd(100)
t.lt(90)| What is there | What happens | Drawing |
|---|---|---|
| the definition | creates a function object | nothing drawn |
| no call | the body never runs | nothing drawn |
| result | an empty window | no error |
Predict first
This is the whole program, after the turtle setup. What appears in the window?
Correct: Nothing — the function is defined but never called, so its body never runs.
Why: This is lesson 3b's rule meeting the turtle: a definition creates a function object and runs nothing. The loop is inside the body and the body is not executed, so no fd or lt call ever happens. The fix is one line, square(bob), and the symptom — a correct-looking program that draws nothing — is exactly the one to associate with a missing call.
Worked example
The book asks this question directly. The answer is the point of the whole exercise.
def square(t):
for i in range(4):
t.fd(100)
t.lt(90)
alice = turtle.Turtle()
square(bob)
square(alice)| Call | What t refers to | Effect |
|---|---|---|
| square(bob) | t refers to bob for this call | bob draws a square |
| square(alice) | t refers to alice for this call | alice draws a square |
| the parameter name | never mentions either turtle | works for both |
Ask the book's question.
Why: Inside the function, t refers to the same turtle bob, so t.lt(90) has the same effect as bob.lt(90). In that case, why not call the parameter bob?
Answer it.
Why: The idea is that t can be any turtle, not just bob. Naming the parameter bob would suggest a connection that does not exist.
Test the answer.
Why: Creating a second turtle and passing it works immediately, with no change to the function. If the parameter had been named bob, the function would still work — and it would read as though it only applied to one turtle.
Figure (svg): A stack diagram showing main holding bob and alice, and two successive square frames whose parameter t refers to each in turn
Because t can be any turtle. The parameter name describes what the function needs — a turtle — rather than which particular one a caller happens to have, and naming it bob would be a misleading comment that Python does not check.
Verify: Rename the parameter to bob and check that the program still works.
Why: It does, which is the interesting part: the name has no effect on behaviour at all. Its entire value is in what it tells a reader, which makes this a documentation decision rather than a functional one — and lesson 3b's point about parameter names being independent is what makes that true.
Trap
Wanting to be organised, a student writes the function definition first and fills in the body from scratch.
Treat encapsulation as a way of writing code
Why: It looks like the tidy approach, and tidiness is usually good.
Now two things can be wrong at once: the drawing logic and the function structure. When nothing appears, there is no way to tell which is at fault.
Encapsulation is a move you make on code that already works.
Get a square on the screen first, at the top level
Why: Then you know the fd and lt calls are right, because you have seen the square.
Then wrap it, and check the drawing is unchanged
Why: Any difference is caused by the wrapping, which narrows the search to four lines.
This is step 2 of the development plan in the next lesson, and the plan's first step is explicit: start by writing a small program with no function definitions. Debugging one thing at a time is the entire reason for the order.
Sorting
Three levels of indentation, three meanings.
Sort into buckets
For each line of the encapsulated square program, which level does it belong to?
Elimination
All four are legal. One is clearly right, and the reasons matter.
Eliminate the wrong options
The parameter of square receives a turtle. Which name should it have?
Survives elimination: B
Why: t is the book's choice and it is right for two reasons. It says what kind of thing arrives — a turtle — without claiming which one, and it avoids colliding with the module name. Option D is the subtle one: a parameter name hides any outer name for the length of the function, which is a real hazard whenever you name a parameter after a module or a built-in.
Explain it to yourself
The drawing is identical. Say what actually improved.
Discussion prompt
Encapsulating the square code changed nothing about the picture. Name two concrete things that became possible afterwards that were not possible before.
Hint: Think about what you can now do with three characters of typing.
Answer:
You can draw a second square by writing square(bob) again — one line instead of copying four. One of the benefits of encapsulation is that if you re-use the code, it is more concise to call a function twice than to copy and paste the body.
You can draw with a different turtle, by passing alice. Before encapsulation the turtle's name was hard-wired into every line.
And a third, which the book puts first: it attaches a name to the code, which serves as a kind of documentation. A reader who sees square(bob) knows what happens without reading the loop.
Section
Section 4
Concept
A turtle program has a state you can track with two numbers and an arrow: where the turtle is, and which way it is facing. Every method call changes one of those, and predicting the drawing means keeping both in mind.
bob.fd(100)
bob.lt(90)
bob.fd(50)
bob.rt(90)
bob.fd(100)| Call | What it does | Position and heading after |
|---|---|---|
| fd(100) | east 100 | at (100, 0), facing east |
| lt(90) | turn left | at (100, 0), facing north |
| fd(50) | north 50 | at (100, 50), facing north |
| rt(90) | turn right | at (100, 50), facing east |
| fd(100) | east 100 | at (200, 50), facing east |
Tracking the heading is the part people drop, and it is the part that matters: an fd call means nothing until you know which way the turtle was pointing when it ran.
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 30-30
Picture it
Two horizontal runs joined by a vertical one.
Figure (svg): A turtle path showing a step shape: east, then north, then east again
Notice that a left turn followed later by a right turn of the same size leaves the heading unchanged. Tracking that is often quicker than tracking each turn separately.
Worked example
Work out the drawing on paper first. Then check the trace.
for i in range(4):
bob.fd(100)
bob.lt(90)
bob.fd(50)
bob.lt(90)| Pass | What it draws | Heading after |
|---|---|---|
| pass 1 | east 100, turn, north 50, turn | now facing west |
| pass 2 | west 100, turn, south 50, turn | now facing east |
| pass 3 | east 100, turn, north 50, turn | retraces pass 1 |
| pass 4 | west 100, turn, south 50, turn | retraces pass 2 |
Track one full pass carefully.
Why: Two moves and two turns of 90 degrees each. After one pass the heading has changed by 180 degrees, so the turtle is facing the opposite way.
Work out what the second pass does.
Why: It retraces the first pass in the opposite direction, returning the turtle to where it started.
Conclude what passes 3 and 4 add.
Why: Nothing new. They draw over the lines already there, because the state at the start of pass 3 is identical to the state at the start of pass 1.
Figure (svg): The state of the program after each line of Worked example predicting a shape before running it, drawn as a ladder with one rung per traced line
A rectangle 100 by 50, drawn twice. Passes 3 and 4 retrace passes 1 and 2 exactly, adding nothing visible.
Verify: Change range(4) to range(2) and check the drawing is identical.
Why: It is, which confirms the second half was redundant. Finding that a loop does its work in fewer passes than it runs is a common and useful discovery, and it is only findable by tracking state rather than by looking at the picture.
Prediction
Track position and heading together.
bob.fd(100)
bob.lt(90)
bob.fd(100)
bob.lt(90)
bob.fd(100)
bob.lt(90)
bob.fd(100)| Aspect | What happened | Final state |
|---|---|---|
| after 4 moves | the four sides of a square | back at the origin |
| after 3 turns | heading changed by 270 degrees | facing north |
| net effect | position restored, heading not | a square, ending facing north |
Predict first
After these seven lines, where is the turtle and which way is it facing?
Correct: At the starting point, facing north — the four moves closed the square but only three turns were made.
Why: The position comes back because four equal sides at right angles form a closed loop. The heading does not, because three left turns of 90 degrees add up to 270 rather than 360. This is exactly the difference between the hand-written square and the loop version, and it is why the book prefers the loop despite the extra turn: the loop restores both.
Worked example
Moving without drawing is how you make separate shapes.
square(bob)
bob.pu()
bob.fd(150)
bob.pd()
square(bob)| Line | What happens | Drawing |
|---|---|---|
| square(bob) | draws a square, ends where it started | one square |
| bob.pu() | lift the pen | moves will not draw |
| bob.fd(150) | move 150 east, invisibly | no line |
| bob.pd() | put the pen down | moves will draw again |
| square(bob) | draws a second square | two squares, separated |
Draw the first shape.
Why: The encapsulated square leaves the turtle where it started, facing the same way — which is what makes the next step predictable.
Move without drawing.
Why: Each Turtle is holding a pen, which is either down or up; if the pen is down, the Turtle leaves a trail when it moves. pu and pd stand for pen up and pen down.
Draw the second shape.
Why: With the pen back down, the same function draws a second, separate square 150 pixels to the east.
Figure (svg): A turtle path showing two separate squares with a gap between them where the pen was lifted
Two squares side by side, 150 pixels apart, with no line connecting them. The gap exists because the pen was up during the move between them.
Verify: Remove the pu and pd calls and predict what changes.
Why: A line appears between the two squares — the move that was invisible becomes visible. That single line is the entire difference, which confirms that pu and pd affect only whether moving draws, not where the turtle goes.
Trap
A student draws a triangle, then calls square, and gets a square that is tilted at an odd angle.
Assume each shape starts from a standard orientation
Why: The position is easy to remember; the heading is invisible in the finished drawing.
The triangle loop leaves the turtle facing its starting direction only if it makes ALL its turns — and a hand-written triangle with two turns does not.
Make every shape function leave the turtle exactly as it found it.
Write loops that turn once per side, including the last
Why: That is the extra turn, and this is the reason it is worth its tiny cost.
If a function must move the turtle, say so
Why: Which is a postcondition, and the next lesson makes that idea explicit.
A function that restores the turtle's state is composable: you can call any sequence of such functions in any order and predict the result. One that does not forces the caller to track the heading, which is exactly the kind of detail a good interface hides.
Reverse engineer
The loop is given with two blanks. The drawing was a regular hexagon.
Fill in the blanks
for i in range(6):
bob.fd(60)
bob.lt(60)
Why: A hexagon has six sides, so six passes. The turns must total 360 degrees across those six passes, giving 60 degrees each. Note that this is not the hexagon's interior angle, which is 120 — the turtle turns through the CHANGE of direction at each corner, which is the exterior angle. The general rule, which the book states as a hint for exercise 3, is that the exterior angles of an n-sided regular polygon are 360/n degrees.
Edge cases
The formula keeps working as n grows. Predict what happens to the picture.
Discussion prompt
Using 360/n for the turn, what does the drawing look like when n is 3, when it is 12, and when it is 100 — assuming the side length stays the same?
Hint: Think about the total distance travelled as well as the shape.
Answer:
At n = 3 a triangle, at n = 12 something visibly polygonal but roundish, and at n = 100 something indistinguishable from a circle.
But with a fixed side length the shape also gets much BIGGER: 100 sides of length 60 is a perimeter of 6000 pixels, which will not fit on the screen.
That is precisely the problem the next lesson's circle function solves. It works out the side length from the radius instead of fixing it, so that increasing the number of sides makes the approximation better rather than making the shape larger.
Explain it
The most common confusion in this chapter, and it is worth being able to resolve.
Discussion prompt
A classmate insists the turn for an equilateral triangle should be 60 degrees, because that is the angle of the corner. Explain why it is 120, using the turtle's own point of view.
Hint: Ask what the turtle is turning FROM and TO.
Answer:
Ask them to walk it. You walk along one side facing one way, and along the next side facing a different way. The turn is the change in YOUR direction, not the angle of the corner you are standing on.
At a 60-degree corner, the direction changes by 120 degrees — the two add to 180 because the corner angle and the turn are supplementary.
The general check that settles it: whatever the shape, walking all the way round it once must turn you through 360 degrees in total. Three turns of 120 gives 360; three turns of 60 gives 180, which would leave you facing backwards rather than home.
Section
Section 5
Concept
The book sets five exercises here and says explicitly: they are meant to be fun, but they have a point too, and while you are working on them you should think about what the point is. The point is the process.
Each exercise is a small step from the one before, and each step is an instance of a named move. Following the sequence is the point: it is a worked example of how a set of functions that work together comes into existence.
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 31-31
Picture it
Each arrow is one of the process moves the chapter names.
Figure (svg): A chain showing the square code becoming square of t, then square with a length, then polygon, then circle
This lesson has done the first two arrows. The next lesson does the rest, and then names the whole sequence as a development plan you can apply to any program.
Worked example
Encapsulate the square. The care is in the order of the steps, not in the code.
def square(t):
for i in range(4):
t.fd(100)
t.lt(90)
square(bob)| Step | What you do | What you learn |
|---|---|---|
| write it | wrap working code, replace bob with t | the function exists |
| call it | square(bob) | a square appears |
| test it | create alice, call square(alice) | a second square appears |
Wrap the working loop.
Why: Indent it, add a header, and change bob to t throughout the body. Missing one bob is the classic error and produces a function that ignores its parameter.
Call it with bob.
Why: This confirms the encapsulation did not break anything: the drawing should be identical to before.
Call it with a second turtle.
Why: This confirms the parameter is actually being used. A function that still says bob inside would draw both squares with bob and the second call would look wrong.
Figure (svg): The state of the program after each line of Worked example exercise 1, done carefully, drawn as a ladder with one rung per traced line
A function that draws a 100-pixel square with any turtle it is given. The two tests check two different things: that nothing broke, and that the parameter is real.
Verify: Deliberately leave one bob in the body and see what happens.
Why: square(alice) then draws three sides with alice and one with bob, producing a visibly wrong picture. Causing this on purpose once makes the symptom recognisable, and it is a symptom you will meet again whenever a parameter is only partly used.
Ranking
Each one depends on the one before. Put them in order.
Put in order
Why: Encapsulate first, because you need a function before you can add parameters to it. Then generalize twice, each time adding one parameter, so that only one thing changes per step. Then build circle on top of polygon rather than from scratch. The discipline of changing one thing at a time is what makes each step testable, and it is the reason the exercises are in this order rather than jumping straight to the general case.
Worked example
The book supplies the geometry. Understand it rather than copying it.
# the hint: exterior angles of an n-sided regular polygon
# are 360/n degrees
angle = 360 / 3
angle = 360 / 4
angle = 360 / 6| Number of sides | The calculation | The turn |
|---|---|---|
| n = 3 | 360 / 3 | 120 degrees per turn |
| n = 4 | 360 / 4 | 90 degrees per turn |
| n = 6 | 360 / 6 | 60 degrees per turn |
Understand why it is 360 divided by n.
Why: Walking once round any closed shape turns you through 360 degrees in total. With n equal turns, each must be 360/n.
Check it against a shape you know.
Why: For a square, 360/4 is 90 — which is exactly the turn you have been using all lesson. The formula agrees with the special case you already trust.
Note the type of the result.
Why: Division always produces a float, so angle will be 90.0 rather than 90. That is harmless for the turtle, and it is the lesson 1b rule showing up in real code.
Figure (svg): The state of the program after each line of Worked example reading the hint for exercise 3, drawn as a ladder with one rung per traced line
The turn is 360/n degrees, which gives 120 for a triangle, 90 for a square and 60 for a hexagon. It follows from the fact that one trip round a closed shape is a full turn.
Verify: Check the formula's prediction against a shape you have already drawn.
Why: The square loop used 90, and 360/4 is 90. A formula that reproduces a case you have already seen working is a formula you can use on cases you have not — which is the only honest reason to trust it.
Trap
The book puts the solutions in the sections immediately after the exercises, and it is easy to read straight on.
Treat the exercises as illustrations of what follows
Why: They look like worked examples, and the layout does not stop you.
The book is explicit about this: the following sections have solutions, so do not look until you have finished, or at least tried.
Attempt each exercise before reading its solution, even briefly.
Write something that runs, however clumsy
Why: The solutions are worth far more once you have a version of your own to compare them against.
Then read the solution for the MOVE, not for the code
Why: The book's square is not much better than yours. What it demonstrates is the reasoning about parameter names and interfaces.
This matters more here than in earlier chapters. The subject of this chapter is a process, and a process can only be learned by running it, not by reading a transcript of somebody else running it.
Prediction
Reason about the parameters before writing the function.
Predict first
To draw any regular polygon, what is the minimum a function needs to be told?
Correct: The turtle, the number of sides, and the side length — the turn angle can be computed from the number of sides.
Why: The angle is 360/n, so passing it as well would be redundant and would let a caller supply an inconsistent pair. This is a real design decision rather than a matter of counting: a parameter that can be derived from the others should usually be derived rather than demanded. It also foreshadows the next lesson's interface design section, where the same reasoning decides that circle should take a radius and not a number of segments.
Missing information
Exercise 4 gives a hint that is really a design constraint.
Discussion prompt
The hint says: figure out the circumference of the circle and make sure that length times n equals the circumference. What does that constraint guarantee, and what does it leave open?
Hint: There are two unknowns and one equation.
Answer:
It guarantees that the polygon's perimeter equals the circle's circumference, which is what makes the polygon the right SIZE rather than merely the right shape.
It leaves n open. Any number of sides works, as long as the length is adjusted to match — fifty short sides or ten long ones both satisfy the constraint.
So choosing n is a separate decision, and it is about how good the approximation should be rather than about correctness. That is exactly the question the next lesson's interface design section is about, and the book's answer — choose n from the circumference rather than fixing it — is a genuinely instructive piece of reasoning.
Real world
The book asks you to work this out while doing them. Answer it now.
Discussion prompt
The book says the turtle exercises have a point and invites you to work out what it is. Having done the first two, what do you think it is — and why use drawings rather than, say, arithmetic?
Hint: Consider what happens when one of these functions is wrong.
Answer:
The point is the process: encapsulate, generalize, design the interface, refactor. The drawings are a vehicle for practising a sequence of moves that applies to any program.
Drawings are used because the feedback is immediate and unambiguous. A wrong angle produces a visibly wrong shape, so you see the bug rather than having to reason about a number — which makes the process learnable without the debugging getting in the way.
There is a second reason: a picture makes a semantic error visible. A program that computes the wrong number silently looks fine; a program that draws the wrong shape cannot hide. For a chapter about growing a program in steps, being able to see instantly whether a step broke anything is exactly what you want.
Comparison
Fill the blanks. Each does the same job at a different scale.
Comparison matrix
| Technique | How it repeats | When it is the right choice |
|---|---|---|
| copy the lines | you write them out several times | almost never — but it is a fine first draft |
| a for loop | one body, run several times in a row | when the same statements repeat immediately |
| a function | one body, called from wherever you like | when the same statements are wanted in several places |
The bottom two are not rivals. The square function contains a loop, because it needs both: the sides repeat immediately, and the square is wanted in several places.
Pattern
Five steps, and the first one is the one people skip.
Step 5 checks that you broke nothing. Step 6 checks that you achieved something. They are different questions and both are worth asking, because a function that ignores its parameter passes the first test and fails the second.
Python documentation — turtle — Turtle graphics turtle — Turtle graphics
Check
Count the passes, then the statements in the body.
for i in range(5):
bob.fd(20)
bob.lt(72)| Aspect | Calculation | Result |
|---|---|---|
| passes | range(5) | 5 |
| turns per pass | one lt call | 5 turns total |
| total turning | 5 times 72 | 360 degrees |
Check your understanding
What shape does this draw, and where does the turtle finish?
Answer: A
Why: Five sides with five turns of 72 degrees each gives 360 degrees of total turning, which closes the shape and restores the heading. Since 360/5 is 72, this is the regular-polygon formula with n = 5, so the result is a regular pentagon and the turtle comes home facing east.
Check
One of these is not a method call.
Check your understanding
Which of these is NOT a method call on a turtle object?
Answer: C
Why: turtle.Turtle() is a function inside a MODULE, reached with the same dot notation but not associated with any object — it is what creates one. The other three are methods: each is called on bob, a particular Turtle object, and each asks that object to do something.
Check
The function was encapsulated carelessly. Find the consequence.
def square(t):
for i in range(4):
bob.fd(100)
t.lt(90)| Line | Which turtle it affects | Consequence |
|---|---|---|
| bob.fd(100) | uses bob, not the parameter | always moves bob |
| t.lt(90) | uses the parameter | turns whichever turtle was passed |
| square(alice) | bob moves, alice turns | a broken picture |
Check your understanding
What happens when this function is called as square(alice)?
Answer: C
Why: One occurrence of bob was not replaced with t, so the forward moves go to bob while the turns go to whichever turtle was passed. bob draws four overlapping lines eastward without turning, and alice turns on the spot without moving. This is the classic incomplete-encapsulation bug, and it is why the procedure says to test with a second turtle.
Real world
Encapsulation is a move you have made before, without a name for it.
Discussion prompt
Think of something you do repeatedly that you have at some point written down as a named procedure — a checklist, a recipe, a set-up routine. What did naming it buy you, and what did you have to decide about which details to leave adjustable?
Hint: The adjustable details are parameters.
Answer:
Naming it buys the same three things as here: you can refer to it in one phrase, you can hand it to somebody else, and when it changes you change it in one place.
The adjustable details are exactly parameters. A recipe that says serves four has hard-coded a number; one that scales has generalized that number into a parameter.
And the question of which details to make adjustable is interface design, which is the next lesson. Too few and the procedure is inflexible; too many and nobody can remember how to invoke it.
Commit first
Answer, then rate your confidence. Both counts matter.
Predict first
A for loop over range(4) has a body of three statements, one of which is a call to a function whose body has two statements. How many statements run in total, not counting the loop header?
Correct: Twenty — the body's three statements run four times, giving twelve, and the function's two statements also run four times, giving eight more.
Why: The counting has two levels. Three statements times four passes is twelve. But one of those three is a call, and each call runs two more statements, so that is four calls times two statements, or eight. Twelve plus eight is twenty. Getting twelve means you counted the call as one statement and forgot it does work; getting twenty-four means you counted the function's body as running on its own as well as inside the loop. Combining a loop with a call is the first time these two counting rules have to be applied together, and it is worth doing slowly once.
Explain it
The parameter-naming question is the one worth being able to answer.
Discussion prompt
A classmate has written def square(bob) and asks why the book uses t instead, since it works either way. Give them the reason, and then give them the two-line experiment that makes it concrete.
Hint: The experiment involves a second turtle.
Answer:
Say: it works either way, so this is about what the name TELLS a reader. Calling it bob implies the function only works with bob, and it does not.
The experiment: alice = turtle.Turtle() then square(alice). It draws a square with alice, proving the function was never about bob — and if the parameter is called bob, that line reads as nonsense even though it works.
Then point at the deeper version: the name is the only documentation Python does not check. It costs nothing to be wrong and it misleads every future reader, which makes it worth a moment's thought even for a one-letter name.
Exit ticket
One honest answer. It decides what the next lesson opens with.
Predict first
Which of these is still least solid for you?
Correct: Whichever you picked is the right answer — this one is for you, not for a mark.
Why: Methods will feel arbitrary until chapter 15, and that is fine — the book introduces them eleven chapters early on purpose, so that they are familiar by the time they are explained. The for statement gets a proper treatment in chapters 7 and 8; here it is a tool. Tracking the heading is the skill that makes turtle programs predictable and it is worth practising on paper. And encapsulation is the process move this whole chapter exists for, so if it is the shaky one, the next lesson's development plan is the place to look.
Connect it up
One page, from memory.
Draw it
Draw the square-drawing code three times: as a bare sequence of seven statements, as a loop, and as an encapsulated function. Beside each, write what it can and cannot do — can it be repeated, can it use a different turtle, can it be a different size. Then draw an arrow between each pair and label it with the process move that gets you there, and circle the one thing that changed about the DRAWING across all three versions.
Recap
Four pages, and the first two moves of the process this chapter exists to teach.
| If you remember one thing | It is this |
|---|---|
| From methods | The dot means the same thing as always: look inside the thing on the left. |
| From the for statement | A loop comes back to where it just was. A call comes back to somewhere new. |
| From the turtle | Track the heading, not only the position. An fd means nothing without it. |
| From encapsulation | Wrap code that already works, then test that the parameter is really used. |
| From the exercises | The point is the process, not the drawings. |
The next lesson finishes the case study: adding parameters to make a function general, deciding what belongs in an interface and what does not, factoring shared code out into polyline, and writing the docstring that documents the contract.
Think Python, 2nd edition — Allen B. Downey §4.1-4.4, pp. 29-32 — everything on these slides traces back here
Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.