Topic #3 – Functions¶
Functions¶
Script/program files are a nice way to organize many statements
- You eventually find yourself writing the same series of statements over and over
(or cutting and pasting in your editor)
Imagine the following code is super important:
x = 10 * 2 y = x/4 print(y)
OK, good, that wasn’t so bad
But now your boss says that you need to do the same calculation two more times, but with 9 and then 8 instead of 10
Now your program looks like this:
x = 10 * 2 y = x/4 print(y) x = 9 * 2 y = x/4 print(y) x = 8 * 2 y = x/4 print(y)
That wasn’t soooooo bad, but it does feel somewhat silly to copy/paste the code
Your boss can’t make up their mind, and they now say that it should be divided by 3 instead of 4…
So you change your code:
x = 10 * 2 y = x/3 print(y) x = 9 * 2 y = x/3 print(y) x = 8 * 2 y = x/3 print(y)
- Fixed.
While doing so, you actually missed the 2nd /4, but you fortunately caught your mistake in time
Your boss comes back again, saying that it needs to be done 18 more times, and they actually liked the divided by 4
…
There’s gotta’ be a better way!¶
Well, there is
We want a way to group together sequence of statements that we frequently reuse
In Python, we do this with a function. Here’s one now:
def my_function(a_parameter): b = a_parameter * 2 print(b)
Once you’ve defined a function, you can call it exactly the same way you’d call a built-in function like
print()
.- So let’s use our function:
>>> my_function(2) 4
>>> my_function(7) 14
>>> my_function('James') JamesJames
When we call
my_function
, Python executes the statements that make up the function, in order.Functions make code easy to reuse, easy to edit, and easy to read. More importantly they facilitate abstraction.
Quick Activity
Write your own function to do something with math. Honestly, whatever you want.
Function Parameters¶
Note carefully the parameter (
a_parameter
) in the definition ofmy_function
- When you are defining a function, you want the function to be very general
You want it to work with any possible parameter that someone might want to give it
- Imagine we want to write a function to add two numbers together
We want to be able to have this function add any two numbers together
Parameters are like variables. When you call the function, the first thing that happens is the parameter values get set.
To motivate this, let’s go back to our previous example and throw it in a function:
def i_hate_my_boss(): x = 10 * 2 y = x/4 print(y)
This function is kinda’ stuck; it will only ever do multiply 10 by 2, and then divide it by 4…
Instead, we’ll give it parameters:
def i_hate_my_boss(a_value, another_value): x = a_value * 2 y = x/another_value print(y)
And we can call it like this:
i_hate_my_boss(10, 4)
If this is scaring you, chill
Like it or not, you’ve been doing this for years in math class
f(x) = x + 5
This is a math function that takes a parameter
- What happens if you say… f(5)
f(5) = 5 + 5
f(5) = 10
IT’S THE SAME WITH THIS HERE!!
Let’s do one more example with adding two numbers:
def add_print(a, b): print(a + b)
Now that the function is defined, we can call it. Like this:
>>> add_print(5, 2) 7
- The call
add_print(5, 2)
gets handled like this: - Python checks to see if it knows about a function named
add_print
We just defined
add_print
, so it does.
- Python checks to see if it knows about a function named
When we defined it, we told Python it should have two parameters:
a
andb
.Python now takes the values in the call (in this case,
5
and2
) and assigns those
- values to the function parameters
a
andb
. In other words, the first thing Python does in this case is set
a = 5
andb = 2
, just like variables.
Then Python executes the body of the function, with the parameters having their new values.
- The call
What happens if we don’t give it enough, or too many parameters?
Abstraction: first steps¶
Why is abstraction important?
Activity
Write down a “program” to make spaghetti (not in python, like on paper). You can only use the following statements:
locate [object]
grasp [limb]
release [limb]
move_limb_to [location]
wait [time in seconds]
Assume you start from a clean, empty, kitchen.
Activity
Write down a “program” to make spaghetti (not in python, like on paper). You can use plain English prose and assume you are addressing a human being.
You’ve now written programs at two levels of abstraction. Which was easier?
- Functions allow us to build towers of abstraction.
- A low level function might worry about how to set the individual pixels of the display to show the letter
A
. Consider
print()
- A low level function might worry about how to set the individual pixels of the display to show the letter
Would you want to cut-and-paste that code every time you needed to print
A
?Instead, we have a function called
print()
that hides all those messy details from us.We call
print()
,print()
calls other functions, which call other functions, which call other functions…Without organizing things into levels of abstraction writing complex software would be impossibly difficult.
Forget programming. In the rest of your life, learning to think in terms of levels of abstraction is a hugely important skill.
- In fact, think about us.
When you move your arms, did you explicitly think about firing neurons, flexing muscles and moving tendons?
When driving a car, do you think about the pistons firing?
Back to concrete things…¶
The general format for defining a function is:
def function_name(p1, p2, p3, p4, ... ): statement 1 statement 2 ... statement m
function_name
is… the name of the function. This can be almost whatever you want.p1, p2
, etc. are called the parameters, you can have as many as you like and call them almost whatever you want.- You tell Python which statements make up the body of the function by using indentation.
- This is a somewhat unique feature of Python.
And somewhat hated by some people.
Many other languages use pairs like
begin, end
,do, done
or{, }
to delimit the body of a function.
Activity
Write a function catstr
which takes two strings as parameters and then prints out the concatenation of the strings. e.g., if I call catstr('Hello ','world!')
it will print Hello world!
.
Activity
NOTE: This one is tricky but super important to understand. If you’re still stuck after class, be sure to take your time to figure this out.
Now write a function crosscat
that will take four strings and print out the concatenation of the first and third string, and then, on a new line, the concatenation of the second and fourth string. BUT: your function isn’t allowed to use a print
function! You can, however, use your catstr
function.
Execution Flow¶
Python executes one statement at a time
To make sense of programs, we need to know which instruction gets executed when
- In a program, the statements get executed in the order in which they appear in the program, top to bottom of the file
Later, we’ll learn how to jump around
What happens when a function gets called? Let’s trace through this program:
def do_stuff(a, b): c = b * 2 d = (a+4) * 2 c = d + c return c x = 2 y = 3 z = do_stuff(x, y) print(z) print("where am I?")
- So what happens is:
- Program starts at the top, and computer sees that a function is being declared (not called yet)
NOT RUN YET THOUGH!
Computer basically skips down to where the function ends
We assign some values to variables
Python makes a note of where the function is being called from
The flow of execution passes to the function
Python executes each statement in the function, in order
At the end of the function, control returns to the point from which the function was called
Function values¶
Notice how
do_stuff
ended with areturn
statementThe
return
statement tells Python: “return this value to whoever called this function”With
return
, functions evaluate into values.- Consider:
>>> print(do_stuff(2, 2)) 16
>>> print(do_stuff(4, 4)) 24
>>> print(do_stuff(2, 2) + do_stuff(4, 4)) 40
When Python hits a
do_stuff
, it goes and does stuff (executes the function).Because that function ends in a
return
, when execution flow comes back to the calling program, the call todo_stuff
gets replaced with whatever value gotreturn
ed.
Activity
Write a function
no_stuff(a, b)
which is identical todo_stuff(a, b)
except it does not contain areturn
statement.- What happens when you try this?
>>> print(no_stuff(2, 2))
- What happens when you try this?
>>> print(do_stuff(2, 2))
Warning
The difference between a print
and a return
is HUGE, yet, every year this difference ends up being a problem for many students. Make sure to take your time understanding the difference. Take your time. Play around. Remember, playing around with Python is the best way to learn this stuff.
Activity
Write a function compmag(r,m)
to compute, and return, the magnitude of a complex number. It should take the real component of the number as parameter r
and the imaginary component as m
.
Remember that \(|r + mi| = \sqrt{r-2 + m-2}\) (if this looks scary, all I really want you do to is the right hand side of the equation)
Say, does Python have a square root function? How would you find it?
Composition¶
Python functions can be composed just like mathematical functions.
We’ve already seen
print
composed withdo_stuff
- We can nest functions, too:
>>> do_stuff(do_stuff(2, 2), do_stuff(2, 2)) 72
- If you get confused tracing nested functions, just remember:
Functions get evaluted and turned into values
Find a function you can evaluate
Evaluate it
Cross out the function and replace it with the value it returns
Keep doing this until you’re down to one value.
Activity
Figure out the value of do_stuff(do_stuff(2, 2), (do_stuff(2, 2) + do_stuff(4, 4)) )
using only pen and paper. No computers!
Activity
Figure out the value of no_stuff(no_stuff(2, 2), (no_stuff(2, 2) + no_stuff(4, 4)) )
using only pen and paper. No computers!
Variable scope¶
If you set a variable inside a function, it is local to that function.
No other function can see a function’s local variables. They are local. Consider this code:
def do_more(a, b): c = 2*a + b return c
- What happens if I do this:
>>> print do_more(4, 4) 12
>>> print(c) NameError: name 'c' is not defined
Error! But
c
is defined indo_more
! Why did we get an error?Moral of the story: variables have scope. This can actually be a surprisingly delicate concept and we’ll come back to it later.
Optional parameters for functions¶
Sometimes you want a function to have an optional parameter, with a pre-specified default value.
This is done very easily:
def my_function(a, b, c=3): do_stuff()
When you call
my_function(5, 12)
,a
will have value5
,b
value12
andc
value3
.Because we specified a default value for
c
, we don’t have to provide one when we call the function.If we want to override the default though, we can:
my_function(4, 3, 2)
.A reasonable example:
def time_to_fall(d, a=9.807): return math.sqrt(2*d/a)
Import¶
Another practical matter: sometimes you want to make a big library of functions. Maybe related to analysis data from your research.
You’d like to access some of those functions from another program that you’re writing.
- If you put your functions in a file called ‘myfuncs.py’, you can import them into another program like this:
>>> from myfuncs import *
(The
*
here means everything)- You could also use:
>>> import myfuncs
This is my preferred way
- BUT, this adds a namespace. To access a function called
do_stuff
in the filemyfunc
after this style ofimport
, you’d have to type >>> myfuncs.do_stuff(...)
- BUT, this adds a namespace. To access a function called
Import — MORE¶
Can also import other people’s functions
>>> import math
>>> import numpy
Function headers¶
Because so much of our programming consists of pasting together functions… it is of special importance to document what a function does.
We do this with a function header:
def set_up_cities(names): """ Set up a collection of cities (world) for our simulator. Each city is a 3 element list, and our world will be a list of cities. :param names: A list with the names of the cities in the world. :return: a list of cities """ print 1 + 2
The stuff between the
"""
is the function header and should appear immediately after thedef
.It should explain what the function is going to do, in plain English. If I have to read the function code to figure out what it does, your header description sucks.
It should explain every parameter.
If the function returns something, it should explain that too
This might all seem like a lot of extra work. And it is. But it’s less work than trying to figure out how everything works after you’ve been away from the code for 2 months.
You don’t believe me. You’ll leave this course and go write code with no comments. Seriously, you will. You might mean to write comments, but you won’t. You’re just too busy.
Then, at some later point, you’ll have to go back to your code. It won’t have comments. You’ll have no clue how anything works. It’ll take you a day or two just to figure out what you’d done before.
After that happens enough times, you’ll start writing comments.
COMMENTS!!!!!!!!!!!¶
You can add comments to your code in Python with
#
:As soon as Python sees
#
it ignores the rest of the current lineWriting comments makes your code easier to read
Especially 6 weeks later when you have to change it
And especially when someone else has to make sense of your mess
Comments shouldn’t just repeat what’s obvious from reading the code
They should provide a higher level description of what’s happening.
Computer Scientists get real geeky about comments
Physicists immediately go into shock and collapse if they write a single comment
Find a healthy balance that works for you