Pre-MAP Course Website | Pre-MAP GitHub | Google
You have already seen how to use built-in modules (e.g. numpy) in python and the functions that accompany them. Now we will learn how to write our own functions.
Functions in python are of the following form:
def function_name(argument_1, argument_2,..., keyword_argument_1=val1, keyword_argument_2=val2, ...)
Where argument_1
and argument_2
are "arguments" and are required, and keyword_argument_1
and keyword_argument_2
are called "keyword arguments" and are optional. The names of python functions can be any combination of lowercase letters, numbers and underscores as long as they don't start with a number, and as long as they are not already the name of a built-in keyword (i.e. print
). Let's look at a very simple example of a function:
add
functionLet's start with a simple function:
def add(x, y):
"""This function adds x to y."""
return x + y
This function adds the argument x
to the argument y
. You indicate that you're defining a function with the def
statement, then comes the name of the function, then (no spaces here) comes parentheses containing the arguments.
The arguments x
and y
are symbols -- a user could call the function on variables that they define, which need not be called x
and y
. Here, they just define that within the function, you will refer to the first argument as x
and the second as y
.
The return
line needs to be indented with respect to the def
line. Next to the word return
, you write the result that you want the function to output.
The line in triple-quotes is called a docstring. It is documentation, or user instructions. Most Python functions contain information in the docstring that will help you figure out how to use the function.
We can now call this function like so:
def add(x, y):
"""This function adds x to y."""
return x + y
a = 5
b = 10
a_plus_b = add(a, b)
print(a_plus_b)
Note that the variables that are defined within a function (x
and y
in this example), cannot be accessed outside of the function. If you try to print x
at the bottom of the code above, you'll see:
NameError: name 'x' is not defined
because the variable name x
only exists within the function. This concept is called scope.
In the cell below, copy and paste the recipe above for addition. Modify it to multiply two numbers together, and don't forget to change the function name and docstring accordingly.
In [ ]:
That example is just for demonstration purposes, of course. But there are times when you want to do something more complicated. Let's now make a function that does something more complicated - one that takes a list of numbers as its argument, and returns a list of only the even numbers.
def only_evens(list_of_numbers):
"""Take a list of numbers, and return a list of only the even numbers"""
# This is an empty list that we'll append the even numbers onto
even_numbers = []
# Go through each number in the list of numbers
for number in list_of_numbers:
# If this number is an even number:
if number % 2 == 0:
# Append it to the list of even numbers
even_numbers.append(number)
# Then return the number
return even_numbers
Copy and paste the only_evens
function above into the cell below, and try it out using a list of numbers that you can create however you like (make it up!). Test that the function works. Turn to your neighbor and practice explaining how the function works to one another. Group work is encouraged!
In [ ]:
Now why is this useful? This is helpful when you need to do the same procedure a bunch of times. If I wanted to get the even numbers out of 20 lists of numbers, I would have to re-write everything in the function above 20 individual times. However, I can call the only_evens
function with only one line each time that I want to use it, like this:
evens_1 = only_evens(numbers_1)
evens_2 = only_evens(numbers_2)
evens_3 = only_evens(numbers_3)
...
numpy
reviewThis is a good place to remind you that when you have lists of numbers, you could turn them into numpy arrays, and use their special powers to do your work. In Exercise 2, you worked with a program that goes through a list of numbers to tell you which ones are even. You might recall that in the lesson on numpy, in Exercise 6, we figured out which numbers were even and odd for an entire numpy array at once.
Refer back to the numpy lesson, and in the cell below, re-write the only_evens
function to use numpy, instead of a for
loop. Call this new function only_evens_numpy
. Run it on the list of numbers and show that it works.
Hint: Don't forget to import numpy, with the line
import numpy as np
In [ ]:
One of the questions you might be asking yourself is: "why do we use numpy if we can just write the functions ourselves?" One reason is that it's usually faster to write these operations with numpy (see above, the numpy version has fewer lines of code).
The real reason is that numpy is way faster than pure Python without numpy. Let's demonstrate that here.
We're going to use a function in numpy to make a really big list of numbers for this exercise. In the cell below, execute:
lots_of_numbers = np.random.randint(0, 100, 100000)
print(lots_of_numbers)
That will create an array of 100,000 random numbers between zero and 100.
We're now going to run our two only_even
and only_even_numpy
functions on lots_of_numbers
, to see which one is fastest. To time a function in an iPython Notebook, you use the %timeit
magic function, like this:
%timeit only_evens(lots_of_numbers)
%timeit only_evens_numpy(lots_of_numbers)
The output tells you how long it takes to run each function (usually in units of ms=milliseconds).
How much faster is the numpy version? (This is why we use numpy!)
In [ ]:
Let's say only_evens
and only_evens_numpy
were complicated functions that took hundreds of lines of code to write. You might want to keep them in their own Python script (their own file), and import them into the notebook where you use them. In this exercise, we'll practice doing that.
gedit
from your command line, and save a file called mymodule.py
in the same directory as this notebook.only_evens
and only_evens_numpy
and save them into the mymodule.py
file.import mymodule
to import the functions from your module into this python script. mymodule.only_evens
or mymodule.only_evens_numpy
in this notebook. Try that below:
In [ ]:
That's how every Python package is written – it's Python code that you import into your notebook or scripts.
King Arthur and the knights seek refuge in a French castle, but things don't work out so great.
Create a function that takes an input argument, which will be a string containing a statement by King Arthur. The function will return a response from the French knight.
Here's one example:
def frenchmans_response(arthur_says):
"""This function responds to King Arthur."""
if arthur_says == "Well, what are you, then?":
response = "I'm French! Why do you think I have this outrageous accent, you silly king?"
elif aurther_says == "What are you doing in England, then?":
response = "Mind your own business!"
else:
response = "I DON'T WANT TO TALK TO YOU NO MORE."
return response
arthur_quote = "Well, what are you, then?"
answer = frenchmans_response(arthur_quote)
print(answer)
I'm French! Why do you think I have this outrageous accent, you silly king?
Get creative and make your own function, and test that it produces the appropriate output.
In [ ]:
In this next example, you'll write a function that converts brightness measurements of stars in magnitudes to solar luminosities ($L_{\odot}$).
The magnitude system in astronomy derives from a system devised by ancient Greeks (Hipparcus, in particular), who catalogued the brightnesses of stars visually with their eyes. The magnitude system, like the Richter scale for earthquakes or the decibel scale for sound, is a logarithmic scale that measures the brightness of astronomical objects. Because of this strange convention, the smaller the value is in magnitude, the brighter an object is: e.g., a -1.5 magnitude star is brighter than a +2 magnitude star. An increase in magnitude by 2.5 means an object is a factor of 10 dimmer.
Flux (W/m$^2$, or power per unit area) is a unit of measurement you're familiar with for measuring the brightness of objects; the solar flux, or how bright the sun is at Earth's distance, is 1377 W/m$^2$.
Converting between magnitude and flux is easy, although perhaps not intuitive. If two stars have fluxes $F_A$ and $F_B$, their magnitudes $m_A$ and $m_B$ can be related via:
$m_A - m_B = -2.5 \log (F_A/F_B)$
If the sun has an apparent magnitude of -26.8 and flux of 1377 W/m$^2$, write a function that returns the magnitude of a mystery object, given an input of its flux. It should have the structure of something like this:
def flux_to_magnitude(myster_flux):
"""This function returns the magnitude of an object given its flux."""
## Do some awesome coding here
return #the magnitude!
What is brightness of a 100-Watt light bulb when seen from a distance of 10 meters, in magnitudes? (Note: you'll have to convert power or luminosity to flux using the inverse square law!)
In [ ]:
Now let's do the reverse of what we did in exercise 7. Knowing the magnitude of an object, write a function that returns its flux, compared to the sun (i.e., $F_{\mathrm{mystery}}/F_{\odot}$). Hint, you'll have to do a bit of algebra to rewrite the formula in exercise 7.
The skeletals of your function should look something like this:
def flux_to_magnitude(mystery_magnitude):
"""This function returns the brightness of object in units of solar flux, given an input magnitude"""
## Serious coding here
return ##the flux!
Here's a handy table of apparent magnitudes of a few notable objects in the night sky, using the function you wrote, what are their brightnesses in units of solar flux?
Object | Apparent Magnitude |
---|---|
Sun | -26.8 |
Sirius | -1.47 |
Deneb | 1.26 |
Altair | 0.77 |
Betegeuse | 0.45 |
Vega | 0.03 |
Arcturus | -0.06 |
Canopus | -0.72 |
In [ ]: