Functions¶
Introduction¶
In the previous lab, you were exposed to functions. You called functions that were provided, but you did not write any functions yourself. In this lab, you will write your own functions.
By the end of this lab, you should be able to:
- write simple functions and
- call them from the interpreter and from other functions.
The concepts required for this lab are discussed in the Introduction to Functions chapter of the textbook (we also provide an abridged version here). We encourage you to try the exercises before you look at the explanation.
Getting started¶
Open up a terminal and navigate (cd
) to your
cmsc12100-aut-19-username
directory, where username
is your
CNetID. Run git pull upstream master
to collect the lab materials
and git pull
to sync with your personal repository.
Revisit Real Valued Functions and Plots¶
The lab3
folder contains an enhanced solution to the plotting
question from the previous lab. You will now write some new functions
to plot different mathematical functions.
Open
ipython3
in one window and the fileplot_lab.py
in your editor in another window.Load the code in the interpreter (that is, in
ipython3
) usingimport plot_lab
. Don’t forget the autoreload commands!In [1]: %load_ext autoreload In [2]: %autoreload 2 In [3]: import plot_lab
Look at the functions
sinc
andplot_sinc
in your editor and then try calling these functions in the interpreter:In [2]: plot_lab.sinc(1.0) Out[2]: 0.8414709848078965 In [3]: plot_lab.sinc(5.3) Out[3]: -0.1570315928724342 In [4]: plot_lab.plot_sinc(-10.0, 10.0, 0.01)
Write a function
square
that takes a floating point valuex
and returnsx * x
. Give your new function a try.Write a function
plot_square
that plots the newsquare
function. You do not need to write this function from scratch; you can simply make a copy of theplot_sinc
function, change the name toplot_square
, update the docstring, replace the call tosinc
and update the arguments topylab.title
andpylab.ylabel
. Then callplot_square
in the interpreter. How does the plot differ from the plot of thesinc
function? (Remember to save the changes to your file so that they’ll be autoreloaded into IPython)You will notice that the code for
plot_square
andplot_sinc
have a lot in common. Keeping multiple copies of the same code almost always creates trouble. You may fix a bug in one copy but forget to fix it in the other, or find you need a third or fourth version of it. When you find yourself repeating code you should move the common code into a function.Pull out the code that plots the figure into a new function. What arguments and how many of them should this function take?
You need to write one new line of code (and change some indentation) to turn this old code into a function: a function header. The rest of the body of the function can be copied directly from
plot_sinc
and then modified to replace the constants with parameters. While the function will work without them, you should also write function comments for this new function consistent with the course style guide.Rewrite
plot_sinc
andplot_square
to use this new function.plot_sinc
andplot_square
should now be much smaller and simpler. Creating moreplot_whatever
functions should now be much easier too.
You’ll notice that there is still a fair amount of code that is
repeated in plot_sinc
and plot_square
. In fact, the only
difference between the two functions is the call to sinc
versus
square
. Later in the course you’ll see that we can write functions
that take other functions as parameters, which will allow us to
abstract more and further alleviate the need for repeated code.
Importing Python Code vs Running Python Code¶
Before we continue, we’re going to explore an important distinction in how Python runs code from other files.
As we’ve seen in class, and as you’ve seen above, we will usually place the implementation of functions in a Python file, and then import it into the IPython interpreter (or into other Python files that need those functions).
For example, at the start of the lab we did this:
import plot_lab
Doing this “imports” all the code in plot_lab.py
into the interpreter, so that you can run it from there. When you do so, it has to be prefixed by the name of the file you just imported:
plot_lab.sinc(5.3)
However, IPython also allows you to do this:
run plot_lab.py
This also results in the code from plot_lab.py
being loaded into IPython, except we don’t need to include the “plot_lab
” prefix when using any code contained in plot_lab.py
. After using “run” as shown above, we would be able to do something like this in the interpreter:
sinc(5.3)
However, the way the code is loaded uses a slightly different mechanism that also involves running the plot_lab.py
file like a program. In fact, you will sometimes see some extra output when using “run”. In
this case, we actually made plot_lab.py
print a warning message
if you did this:
In [8]: run plot_lab.py
It looks like you're running this code instead of importing it!
Please make sure you read the 'Importing Python Code vs Running Python Code'
section of the lab.
Notice how this is the same output we get when running plot_lab.py
from the Linux terminal:
$ python3 plot_lab.py
It looks like you're running this code instead of importing it!
Please make sure you read the 'Importing Python Code vs Running Python Code'
section of the lab.
Importing will look for definitions in the Python file (function definitions, variable definitions, etc.) and will import them into the interpreter. We can also include an import statement in a Python file so we can access functions from a different Python file (for example, in PA2’s schelling.py
there is an import utility
statement so that we can access functions defined in utility.py
). When we treat Python files in this way, the Python file is typically referred to as a module.
Running will effectively accomplish the same thing, except running can only be done from the terminal or from the IPython interpreter. We cannot include a run utility
statement in schelling.py
to access the code in utility.py
.
Furthermore, running will look for a block of code like this:
if __name__ == "__main__":
And will run the code contained inside that if
. This will not happen when importing. The files for the next two exercises (geometry.py
and list_exercises.py
) include such a block, so that you can experiment
with running them from the Linux terminal.
As a general rule of thumb, any time you need to use code from one file in another one (or in the interpreter), you should always use the import
statement. While it may be tempting to use the run
command in IPython, this will not autoreload changes in your code, which IPython will do if you use import (and activate the autoreload extensions)
For more details on this, we recommend reading the following section of the Python tutorial: https://docs.python.org/3.5/tutorial/modules.html (you should skip 6.1.3 and 6.4)
If you’d like even more details, this page provides a more in-depth discussion of how importing works: https://chrisyeh96.github.io/2017/08/08/definitive-guide-python-imports.html
Geometry¶
In this section you will be asked to write several functions from scratch. You will need to decide what each function requires as input and what it produces as an output.
A point in the real plane can be described by an x coordinate and a y coordinate, written mathematically as the point (x, y). A line segment can be described by two points (x1, y1) and (x2, y2). The length of a line segment can be found by computing the following mathematical formula:
In this section you will create functions to compute the distance between two points and a function to compute the perimeter of a triangle. You will see how one function can be used by another.
Open the file geometry.py
in an editor.
What types can we use to describe a point in Python? What effect does the choice of type have on the number of variables we need to represent a point?
Consider a function
dist
that takes in arguments that describe two points and returns one value that is the distance between those two points. Write the function header for the functiondist
. If you’re not sure how to write the header then ask for help. It is important to get this code right before you move on.After you have written the function header, write the body of the function. Be sure to
return
the correct value. You will need to use the functionmath.sqrt
, which takes afloat
as an argument and returns its square root as afloat
.Verify that your code works by using
dist
in the interpreter with a simple example. For example, compute the distance between the points(0, 1)
and(1, 0)
. You should get a number very close to1.414
.You may also add code to print the output of
dist
, along with its expected output, in thego
function, so that you can check it anytimegeometry.py
is run.Repeat steps 2, 3, 4 with a new function named
perimeter
that takes in enough information to describe three points that define a triangle and returns the perimeter of that triangle. Do not callmath.sqrt
within the body of theperimeter
function.
Useful List Functions¶
Open the file list_exercises.py
in an editor.
Write a function
are_any_true
that takes a list of booleans as arguments and returnsTrue
if at least one of the entries in the list isTrue
, andFalse
otherwise. Add code to the function namedgo
to test your function on a few different lists. Recall that you can initialize lists directly to generate test cases. For example:test_list = [True, True, False, True, True]
Write a function
add_lists
that takes two lists of the same length as arguments and adds corresponding values together[a[0] + b[0], a[1] + b[1], ...]
. The function should return a new list with the result of the addition. (Your code may assume that the lists are of the same length.)Add calls to your function to
go()
. Try out different types for the elements. What happens if the elements are integers? What happens when they are strings? What happens if the lists are of mixed types (e.g.,[5, "a", 3.4]
)?Write a function
add_one
that takes a list and adds1
to each element in the list. This function should update the input list, not create a new one. What value is printed by the following code?
a = [1, 2, 3, 4, 5]
add_one(a)
print(a)
When Finished¶
When finished with the lab please check in your work. Assuming you are inside the lab directory, run the following commands from the Linux command-line:
git add plot_lab.py
git add geometry.py
git add list_exercises.py
git commit -m "Finished with lab3"
git push
No, we’re not grading this. We just want to look for common errors.