Objectives

We'll spend some more time this week learning Python syntax and creating functions, which are collections of code that we may want to execute more than once. An important aspect of functions are parameters, which are a way of making a function do more than one thing.

In addition, we'll focus on overall code organization. How you write and organize your code makes a big difference in how easy it is to write, modify, debug, and re-use. For example, we will be grouping functions that do similar things in the same file, we will have only one copy of each function, and we will be adding comments to our code. Good code organization saves you time and effort.

Tasks

Setup

Mount the Personal fileserver, and make a folder called project2 in your directory. Cmd-K in the Finder is the shortcut to the fileserver connection window.

smb://filer.colby.edu

Open a text editor (e.g TextWrangler). Create a new file called lab2.py and save it in the project2 folder. As you did in the project last week, we're going to put a series of Python commands into the file and then tell python to execute those commands.

Header comments and docstrings

Put a comment at the top of the file with your name, date, and the name of the file. In Python, you make a comment by using a #. Anything on the same line after the # is not read by the interpreter.

Comments are important in code. Comments have three purposes:

From now on we'll be grading your code not only on its functionality, but also on whether it is well-commented and easy to read. Well-commented code has a comment (1 sentence) at the beginning of each function describing what it does and the meaning of its parameters. It will also generally have a comment before each major section of the function.

The comment at the beginning of each function is special. We call it a docstring and use different characters to set it apart from the code. We use three double-quotes at the beginning and at the end. Below is an example of a file with the commenting conventions you should use for all of your projects.

# Bruce Maxwell
# CS 151 Fall 2017
# Lab 2
# uses the turtle to draw a triangle

import turtle

def draw_triangle( edge_len ):
    """ Draw an equilateral triangle
        Each edge should be edge_len long
    """
    turtle.forward( edge_len )
    turtle.left( 120 )
    turtle.forward( edge_len )
    turtle.left( 120 )
    turtle.forward( edge_len )
    turtle.left( 120 )
    
# main code
draw_triangle( 50 )
draw_triangle( 100 )
input( "Press enter when ready " )

It is acceptable to use a regular comment at the start of a function, or above the title. Using the docstring syntax allows Python to attach the comment to the name of the function within its automatic help system.

Import the turtle package

After file header comments, write the command to tell python to import the turtle package:

import turtle

From now on, we'll be importing packages using the above syntax, rather than from turtle import *. It requires you to access the turtle functions slightly differently, but it enforces better code organization and style, in addition to reducing unintended errors.

To test that everything is working, put the following commands in the file and then run it on the command line.

turtle.reset()
turtle.forward( 100 )
input('Press enter to continue')

The above code is pretty much identical to how we started last week, but since we included the turtle package using the import turtle syntax, we have to use the prefix turtle. prior to any function from the turtle package.

Recall that to execute the file in a terminal you need to change the working directory to the one with your file in it by typing cd and then the path to the directory. You can either type the path to your project2 directory in Terminal or you can use the Finder to drag and drop the path to your project folder into Terminal after you type cd and a space.

To run your program, type in the Terminal:

python3 lab2.py

Package Nicknames

Tip: you can rename a package when you import it. For example, you can use the statement:

import turtle as fred

to redefine turtle as fred. To call functions in the turtle package after this declaration you would use the prefix fred instead of turtle:

fred.reset()
fred.forward(100)

Using fred or george is probably not the best idea, but it should make the point that you can rename the turtle package to something shorter and more convenient.

goto( )

Once your file works properly, go back and delete the reset and forward commands. Before the input line, make a function that sends the turtle to a particular location without drawing anything. Name the function goto and give it two parameters to hold the new x location and y location.

Function syntax reminders:

The goto function should raise the pen using the turtle.up() function, use the turtle.goto() function to jump to the location specified by the parameters, and then put down the pen using turtle.down().

Print Statements

While you are still learning to interpret code, it may also be useful to have print statements in your functions that indicate what is happening. Print statements are your most important debugging tool, because you can use them to find out information about both the order of operations and the value of parameters at different times during the process.

At the beginning of your goto() function, put the following print statement.

    print('goto(): going to', x, y)

Once you have written the function, test it by using the goto() function and drawing some lines. For example, after the function definition, but before the input() call, you could do the following:

print('running main code')
goto( 100, 100 )
turtle.forward( 50 )
goto( -100, 50 )
turtle.forward( 50 )

Note the difference between the function goto() and turtle.goto(). Without the turtle prefix, python will call the function you defined. With the turtle prefix, python will call the goto function in the turtle package.

It's also important to start thinking about where functions and top-level code are placed relative to each other in your file. Top-level code should always come after all function definitions. If you intersperse code and functions, it will lead to confusion and be difficult to debug or modify.

Execute your code and make sure your goto() function works properly. Note what it prints out and make sure you understand the order of operations.

block( )

Now we want to make a block function that draws a rectangular shape of a given size in a given location. This function will need four parameters: x location, y location, width, and height. Name the function block, and define it after your goto function. The function should jump to the given location and draw the rectangle with the given width and height. You can call functions from other functions, so your block function will probably start by calling your goto() function. Keep using print statements so you can follow the order in which the computer executes the code.

Copy and paste the following code template into your file. Then select all of the text in your file (Cmd-a) and select the Entab option under the Text menu. This makes sure all of your white space uses tabs. Alternatively you could use the Detab option, which turns all of your white space into spaces.

def block( x, y, width, height ):
    '''Draws a block at (x, y) of the given width and height'''
    goto( x, y )

    print('block(): drawing block of size', width, height)
    # tell the turtle to go foward by width
    # tell the turtle to turn left by 90 degrees
    # tell the turtle to go forward by height
    # tell the turtle to turn left by 90 degrees
    # repeat the above 4 commands

Remove your old main code (the goto() and turtle.forward() commands) and replace them with a couple of calls to the block() function. When we say main code, we mean the code in your file that is not inside a function, also called the top-level code.

A bunch of blocks

Now we want to make a function that draws a bunch of blocks relative to a central location. Name the function bunchOfBlocks. It should take three parameters: x location, y location, and a scalefactor. A scale factor of 1.0 should draw the shape in its natural size.

The first command in the function should be a print statement. The remainder of the function should call the block function 2-4 times, creating blocks in different locations relative to the (x, y) location passed into bunchOfBlocks(). The example below shows the function definition, the print statement, and three calls to the block function. Note the use of the scale factor in both the location offsets and the size of the block.

def bunchOfBlocks( x, y, scale ):
    print('bunchOfBlocks(): drawing blocks at location', x, y)
    
    # put several calls to the block function here
    block( x, y, 45*scale, 90*scale )
    block( x+15*scale, y+90*scale, 15*scale, 30*scale )
    block( x+20*scale, y+120*scale, 5*scale, 15*scale )

The picture below is result of three calls to the bunchOfBlocks function. Note that the relative sizes and locations of the blocks are all identical, despite different scale factors.

Make your own bunchOfBlocks function. If you want, just make some changes to the code above and see what happens. Then change the main code in your file to call the bunchOfBlocks function several times with different x,y, and scale parameters. Then run your file.

Random number generation

Now we can have some fun with our bunchOfBlocks function. At the top of your file, put the following statement.

import random

Replace the main block of code with the following. Note the use of the function random.random() in the x,y, and scale factor. This function returns a random number between 0 and 1.0. The for expression executes the code indented beneath it the number of times specified in the argument to the range function.

for i in range(10):
    bunchOfBlocks( random.random()*600 - 300,
                   random.random()*600 - 300,
                   random.random()*2 + 0.5 )

When you are done with the lab exercises, you may start on the rest of the project.


© 2018 Caitrin Eaton.