6. Functions

Functions Lab

In this lab, we will learn how to create and call functions. Functions are blocks of code that are reusable throughout a program. You’ve already encountered functions such as print(), which is a function built into Python.

What is a function?

Before we can talk about functions, we need to talk about code blocks. A code block is one or more lines of code. Here’s one:

forward(side_length)
left(120)

Sometimes we want to talk about how many times a code block should run, or whether it should run at all. Consider this code, which will draw a triangle:

1
2
3
for each_item in range(3):
    forward(side_length)
    left(120)

We have the same code block, but now it’s indented under the loop on line 1. This means the code block should run once for each item in the range. A few things to note:

  • Whenever a code block is indented, there’s always a line introducing it. This line always ends with a colon.
  • The way you indent matters. Always use the tab key to indent.
  • When you’re done with a code block, just stop indenting.

Often times we want to reuse a code block in different sections of a program. Functions let you give a name to a code block so you can reuse it without having to copy and paste.

1
2
3
4
def draw_triangle(side_length):
    for i in range(3):
        forward(side_length)
        left(120)

A few things to note:

  • The structure of the function definition looks a lot like a for-loop. The introduction on line 1 ends with a colon, just like the for-loop on line 2.
  • The def keyword (line 1) tells the computer you’re defining a new function.
  • The function’s name, draw_triangle, comes right after def.
  • After the function’s name is the list of arguments, surrounded by parentheses (side_length). Arguments are the things you need to tell the program before you can use the function. Think of arguments as questions the program might ask about what to do. For draw_triangle, the program wants to know “How long should the length of the sides be?” and you have to tell it the side_length. When a function doesn’t need any arguments, its argument list will look like ().
  • In the function’s code block, you can use the arguments as variables. The value of the argument will be assigned when the function is called. You could draw a small triangle by running draw_triangle(10) and a big triangle by running draw_triangle(100).
👾 💬 Functions make your code simpler.
Instead of writing all the code for a triangle over and over, you can get it right once and put it in a function. Once you have written a function, you must call it. Calling a function tells Python to run that block of code. Just as you can’t read the value of a variable before it has been defined, you can’t call a function before it has been defined.
✅ CHECKPOINT:

Look at the code below. Answer the questions in your notebook before moving on:

  1. What argument does the hexagon function take?
  2. How is that argument used in the hexagon function?
  3. On which line is the hexagon function called?
  4. On which line is the draw_honeycomb function called?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def hexagon(side_length):
   "Draws a hexagon."
   for i in range(6):
       forward(side_length)
       right(360 / 6)

def draw_honeycomb():
    "Draws a honeycomb pattern of seven hexagons."
    for each_side in range(6):
        pendown()
        hexagon(20)
        penup()
        forward(20)
        left(360 / 6)

draw_honeycomb()

This program will draw the pattern below.


Combine functions

When you want to draw something fancy, you need to break it down into smaller steps. Then you can write functions to do the smaller steps, and write more functions to combine small steps into bigger steps. This concept is called decomposition. (Going the other direction, the process of combining simple behaviors into more complex behaviors is called composition.)

Let’s decompose the complex drawing of an ice cream cone into simple parts:

  • To draw an ice cream cone with num_scoops scoops:
    • Draw the cone:
    • Do this num_scoops times:
      • Move the turtle up.
      • Draw a scoop of ice cream.

Plan

👀 Open ~/Desktop/making_with_code/unit00/lab06/ice_cream_cone.py and read the code. How might these functions work together?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from turtle import *

def prepare_to_draw(x, y):
    "Moves the turtle to (x, y) without drawing and prepares to draw."
    penup()
    goto(x, y)
    pendown()
    speed(0)
    setheading(0)

def draw_cone():
    "Draws an ice cream cone."
    # YOUR CODE GOES HERE

def draw_ice_cream_scoop():
    "Draws one scoop of ice cream."
    # YOUR CODE GOES HERE

def draw_ice_cream_cone(num_scoops):
    "Draws an ice cream cone with scoops on top."
    # YOUR CODE GOES HERE

# Once you finish testing, replace the next line with the commented-out 
# line below, allowing the player to choose how many scoops they want.
choice = 3
# choice = int(input("How many scoops of ice cream would you like?"))
draw_ice_cream_cone(choice)
input("Press return...")
👾 💬 Docstrings
Did you notice how the first line of each function is a string describing the function? These are called docstrings and they don’t have any effect on how your code runs. Docstrings are mostly meant to help programmers understand what the function is for, but there are some fancy Python programs which read other Python programs and automatically extract docstrings to create a description of the program.

Code

💻 Replace the comment on line 21 with code implementing the draw_ice_cream_cone function. Use draw_cone and draw_ice_cream_scoop as if they were already working–we’ll take care of them next. Let’s elaborate the pseudocode we wrote above, bringing it a bit closer to Python:

  • To draw_ice_cream_cone with num_scoops scoops:
    • Set scoop_offset to 50 (This controls how much higher each scoop should be.)
    • draw_cone
    • For scoop_count in range(num_scoops):
      • Set scoop_y to scoop_count * scoop_offset
      • prepare_to_draw(0, scoop_y)
      • draw_ice_cream_scoop()

Once you finish translating this pseudocode, draw_ice_cream_cone still won’t do anything because it relies on draw_cone and draw_ice_cream_scoop to do the actual drawing, and these functions don’t do anything yet. Implement them now. Even if the cone and the scoops don’t look great at first, you will see the general pattern of the ice cream cone taking shape: one cone and three scoops.

👾 💬 How to fill a shape with color

To fill your turtle drawing with color, use the begin_fill() and end_fill() functions as shown below. This chart lists recognized color names.

def draw_triangle(sideLength):
    #This function draws a triangle
    for i in range(3):
        left(120)
        forward(sideLength)

fillcolor("Blue")
begin_fill()        #Tells the turtle to start the color fill
draw_triangle(20)
end_fill()          #Tells the turtle to stop the color fill

Design your own functions

In this exercise you will need to write some functions on your own. Our aim is to create a grid of squares.

Plan

👀Open grid.py and read the code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from turtle import *

def prepare_to_draw(x, y):
    "Moves the turtle to (x, y) without drawing and prepares to draw."
    penup()
    goto(x, y)
    pendown()
    speed(0)
    setheading(0)

def grid_of_squares(side_length, margin, rows, columns):
    """Draws a (columns * rows) grid of squares.
    Each square has size `side_length`, with `margin` between squares.
    """
    # YOUR CODE HERE

#Keeps the drawing window open until key press  
input("Press return to end drawing...")
👾 💬 Triple quotes
When you need a string to be longer than one line (as in the verbose docstring on lines 12-14), use triple-quotes. Everything between pairs of triple-quotes is contained within a single string.

The grid_of_squares function looks like a lot of work. We will need to decompose this problem into simpler subproblems. Here is one way you could do it:

  • To draw a grid of squares, we need to draw a bunch of rows of squares, each at a certain y position.
  • To draw a row of squares, we need to draw a bunch of squares, each at a certain x position.
  • To draw a square, we need to draw four sides of the correct length.

If you use this decomposition strategy, you’ll need to write several new functions:

  • row_of_squares(side_length, margin, number_of_squares)
  • square(side_length)

Think about the arguments for each of these functions. Why does each function need to know this information?

Code

Add the functions row_of_squares(side_length, margin, number_of_squares) and square(side_length) to your program and implement them. Now that you have decomposed the problem, it might be a good idea to compose the solution, starting from the simplest pieces. Make sure your square function works, then see if you can get row_of_squares working.

Then use row_of_squares to write grid_of_squares.


Extension

Ice cream parlor

Let’s return to ice_cream.py.

Right now, both the color of the ice cream and the number of scoops are pre-determined or hard-coded. If you wanted to change the color or number of scoops, you would have to go back into the code and change it yourself.

💻 Expand the functionality of this program to simulate an ice cream parlor. The user should be able to choose the flavor of the ice-cream and the number of scoops.

👾 💬 Hints

Consider the following:

  • How will you implement the functionality of a flavor that is not hard-coded?
  • How will you include user input?
  • How will you translate the flavor “chocolate” to the color “brown?

Here is an example interaction:

python3 ice_cream.py

--- Welcome to the ice cream parlor ---

What flavor would you like?
   > Select a flavor (chocolate, strawberry, vanilla): chocolate
How many scoops would you like? 
   > Select number of scoops (max 3): 3

--- Enjoy your ice cream! Please come again! ---

[Press any key to exit]

Custom grid

Let’s return to grid.py.

💻 Expand the functionality of this program so the user can customize the size and colors in the grid. For example:

$ python3 grid.py

--- PATERN GENERATOR ---

How many rows?
   > Enter a number: 5
How many columns?
   > Enter a number: 3
Pick a primary color.
   > Choose a color (darkseagreen, coral, deeppink): darkseagreen
Pick a secondary color.
   > Choose a color (darkred, , cyan, cadetblue): coral

[Press any key to exit]