This chapter presents a case study that demonstrates a process for designing functions that work together. It introduces the turtle module, which allows you to create images using turtle graphics.The turtle module is included in most Python installations.

https://docs.python.org/3/library/turtle.html

The turtle module

import turtle
leo = turtle.Turtle()

Create a file named my_polygon.py and type in the following code:

import turtle
leo = turtle.Turtle()
print(leo) #This tells us that leo refers to an object with type Turtle as defined in module turtle.
turtle.mainloop()

mainloop tells the window to wait for the user to do something, although in this case there’s not much for the user to do except close the window.

Then we call a method to move the small arrow around the window.

leo.fd(100)

Calling a method is like making a request: you are asking leo to move forward. The argument of fd is a distance in pixels, so the actual size depends on your display.

To draw a right angle, add these lines to the program (after creating leo and before calling mainloop):

leo.fd(100)
leo.lt(90)
leo.fd(100)

Exercise 01

Now modify the program to draw a square.

Simple repetition

We can do the same thing more concisely with a for statement.

A for statement is also called a loop because the flow of execution runs through the body and then loops back to the top.

for i in range(4):
    print('Hello!')

Exercise 02

The following sections have solutions to the exercises, so don’t look until you have finished (or at least tried).

1. Write a function called square that takes a parameter named t, which is a turtle. It should use the turtle to draw a square. Write a function call that passes leo as an argument to square, and then run the program again.

2. Add another parameter, named length, to square. Modify the body so length of the sides is length, and then modify the function call to provide a second argument. Run the program again. Test your program with a range of values for length.

3. Make a copy of square and change the name to polygon. Add another parameter named n and modify the body so it draws an n-sided regular polygon. Hint: The exterior angles of an n-sided regular polygon are 360/n degrees.

4. Write a function called circle that takes a turtle, t, and radius, r, as parameters and that draws an approximate circle by calling polygon with an appropriate length and number of sides. Test your function with a range of values of r. Hint: figure out the circumference of the circle and make sure that length * n = circumference.

5. Make a more general version of circle called arc that takes an additional parameter angle, which determines what fraction of a circle to draw. angle is in units of degrees, so when angle=360, arc should draw a complete circle.

Encapsulation

def square(t):
    for i in range(4):
        t.fd(100)
        t.lt(90)

square(leo)

Inside the function, t refers to the same turtle leo, so t.lt(90) has the same effect as leo.lt(90). In that case, why not call the parameter leo? The idea is that t can be any turtle, not just leo, so you could create a second turtle and pass it as an argument to square:

raphael = turtle.Turtle()
square(raphael)

Wrapping a piece of code up in a function is called encapsulation.

Generalization

def square(t, length):
    for i in range(4):
        t.fd(length)
        t.lt(90)

square(leo, 100)

Adding a parameter to a function is called generalization because it makes the function more general: in the previous version, the square is always the same size; in this version it can be any size.

def polygon(t, n, length):
    angle = 360 / n
    for i in range(n):
        t.fd(length)
        t.lt(angle)

polygon(leo, 7, 70)

When a function has more than a few numeric arguments, it is easy to forget what they are, or what order they should be in. In that case it is often a good idea to include the names of the parameters in the argument list:

polygon(leo, n=7, length=70)

These are called keyword arguments because they include the parameter names as “keywords”. https://docs.python.org/3/tutorial/controlflow.html#keyword-arguments

This syntax makes the program more readable. It is also a reminder about how arguments and parameters work: when you call a function, the arguments are assigned to the parameters.

Interface design

import math

def circle(t, r):
    circumference = 2 * math.pi * r
    n = 50
    length = circumference / n
    polygon(t, n, length)

The interface of a function is a summary of how it is used: what are the parameters? What does the function do? And what is the return value? An interface is “clean” if it allows the caller to do what they want without dealing with unnecessary details.

Rather than clutter up the interface, it is better to choose an appropriate value of n depending on circumference:

def circle(t, r):
    circumference = 2 * math.pi * r
    n = int(circumference / 3) + 1
    length = circumference / n
    polygon(t, n, length)

Refactoring

def arc(t, r, angle):
    arc_length = 2 * math.pi * r * angle / 360
    n = int(arc_length / 3) + 1
    step_length = arc_length / n
    step_angle = angle / n
    
    for i in range(n):
        t.fd(step_length)
        t.lt(step_angle)

The second half of this function looks like polygon, but we can’t reuse polygon without changing the interface. We could generalize polygon to take an angle as a third argument, but then polygon would no longer be an appropriate name! Instead, let’s call the more general function polyline:

def polyline(t, n, length, angle):
    for i in range(n):
        t.fd(length)
        t.lt(angle)

Now we can rewrite polygon and arc.

def polygon(t, n, length):
    angle = 360.0 / n
    polyline(t, n, length, angle)

def arc(t, r, angle):
    arc_length = 2 * math.pi * r * angle / 360
    n = int(arc_length / 3) + 1
    step_length = arc_length / n
    step_angle = float(angle) / n
    polyline(t, n, step_length, step_angle)

Finally, we can rewrite circle:

def circle(t, r):
    arc(t, r, 360)

This process - rearranging a program to improve interfaces and facilitate code reuse - is called refactoring. In this case, we noticed that there was similar code in arc and polygon, so we “factored it out” into polyline.

docstring

A docstring is a string at the beginning of a function that explains the interface (“doc” is short for “documentation”). Here is an example:

def polyline(t, n, length, angle):
    """Draws n line segments with the given length and
    angle (in degrees) between them. t is a turtle.
    """    
    for i in range(n):
        t.fd(length)
        t.lt(angle)

Exercise 03

1. Write an appropriately general set of functions that can draw shapes as below. Tips: draw 60 squares, turning right 5 degrees after each square.

2. Write an appropriately general set of functions that can draw shapes as below. Tips: draw 60 squares, turning 5 degrees after each square and making each successive square bigger. Start at a length of 30 and increment 4 units every square.

3. Write an appropriately general set of functions that can draw shapes as below.

4. Write an appropriately general set of functions that can draw any other kind of spiral, such as an Archimedian spiral. Read more about spirals at http://en.wikipedia.org/wiki/Spiral.

Exercise 04 (optional)

Write an appropriately general set of functions that can draw shapes as below. The third shape is optional.