Tutorial, Part 2: Functions and modules, focus on numpy

Contact: compwiki@physics.utoronto.ca

Last updated around: 2017-07-25

Migrating compwiki.physics.utoronto.ca from Python 2 to Python 3 is ongoing and we apologize for any out of date documentation. Please see the background information for a couple of tips on how to convert from Python 2 to Python 3. We will try to let you know if any links point to non-updated documentation.

1. Review

Congratulations on completing Part 1 of the tutorial! Let's review what we've learned so far:
  1. Spyder is an integrated development environment (IDE) for Python. It lets you try out commands one at a time in the Python shell or put several commands together in scripts that can be run with Run → Run Module or with F5. Python programs ("scripts") should be saved with the .py extension.
  2. You can use the Python console interactively, for example to test commands or to do simple calculations. You need to watch out for Python's precedence rules and rules on integer division.
    • This is particularly tricky when converting old scripts from Python 2 to Python 3.
  3. You can create variables that stand in for certain values, and write formulas like

    that are easier to understand. The = sign is an assignment operator that assigns values to the right of = to the variable to the left of the =. We also learned about incrementing variables with assignment statements like a /= 2.
  4. We practiced with a few examples, using print statements and comments to make it clear what we were doing.

Let's practice what we've learned, and add a couple of little tricks, with an example from orbital mechanics. According to Kepler's Third Law (see Knight, Second Edition, Chapter 13.6) the orbital period T of a satellite (that is, a very light mass) under the influence of the gravity from a massive body M, such as the Earth or the sun, at a distance r from the centre of M, satisfies
where G is the gravitational constant. One effective way to use computers in physics is to code up simple algebraic formulas like this and test them for various input values.

Activity 1: Now do the following:
  • Write a program that will calculate the orbital period in years of a satellite at a given distance r from a massive body M. Have the program output the period in years and the speed in m/s of the object. To make the program user friendly, ask the user to input the values for r and M. Compare your answers on the period to data found in Table 13.2 of Knight, Second edition. You can use the value π ≈ 3.14159; later we will learn how to access the constant π for mathematical calculations.
  • You will need a couple of new tricks to get this task done. Use the python command input('prompt') to get the user to input values for r and M. The command input('prompt') displays a prompt string and then waits for input from the user. Here is a template for using input('prompt'):
    r_string = input('Please enter the radius in metres. ') #your input will result in a string being assigned to r_string
    r = float(r_string) #will convert the string to a float

    This will print Please enter the radius in metres., and then wait for you to input a value and press return. The second line converts what you input into a float. The value you input will be assigned to the variable r in the python script.
  • If you wish to output the value of the angle of separation, it will be useful to print it as a number between 0 degrees and 360 degrees. To do so, you will need to use the mod operator %, which returns the remainder from the division of two numbers. For example 5%2 has the value 1, and 3.2%1.5 has the value 0.2.
  • Another useful trick is to use scientific notation ("e") format for the numbers in your program. For example, the gravitational constant

    would be written as the assignment statement G=6.673e-11 in Python.
  • Give it a try and compare your script to the one we came up with, which you can download here: . The output of a typical session, with parameters appropriate for Earth, can be found here: .
Good programming practice: comments and input-output . Notice that our solution script has a lot of comments and the input and output statements are written to be understood by any user. Follow this practice so that anyone, including yourself in several months time, can understand the program. Quality and clarity of your code, comments, and output will factor into our evaluation of your computational work.

We are now ready to begin the next part of the tutorial, which covers the important concepts of functions and modules. Before we do, we wish to remind you that this is not a formal course in Python. So while we will be introducing a lot of concepts here, we do not expect you to learn them thoroughly at this point. Instead, we want to make sure you get a working knowledge and lots of practice. So don't get discouraged if you don't understand all the examples; your understanding will improve as you work more with Python.

2. Introduction to functions and modules; the numpy module

The expression input('Please enter the radius in metres. ') in the previous example is a call to a Python function called "input", which takes an argument in parentheses (), performs an action, and outputs a value.

Besides built-in functions like input(), you can get access to functions other people have written, and, as we'll show later, you can define your own functions.
Modules are Python programs that package together several functions, as well as variables, for you to access and use. Using modules allows you to extend Python, reuse code, and take advantage of code created by others. So functions and modules can reduce the amount of coding you have to do.

Before we learn more about functions, we will start with a simple example of using modules. Lots of formulas in physics use Euler's constant, e=2.718.... This constant is used so often that it has already been defined in one of the standard Python modules. We can find it in an important module named numpy (which is a contraction of "numerical python"). To access e. we can import it from numpy.

Activity 2: Now do the following:
  • Open Spyder and go to the Console frame.
  • At the prompt type
    In [1]: e

    You will probably see something like the following error message:
    In [8]: e
    Traceback (most recent call last):
      File "<ipython-input-8-9ffbf43126e3>", line 1, in <module>
    NameError: name 'e' is not defined

    This error happened because the interpreter knew nothing about e.
  • Now try to load e as a variable by typing
    In [12]: from numpy import e
  • What happens? Nothing, apparently, but at least you didn't get an error message! Now type
    In [13]: e

    You should see
    Out[13]: 2.718281828459045

    The import step let the interpreter learn about the constant e.
    • Predefined constants in Spyder (iPython): Note that in Spyder (as part of its foundation in the iPython framework) several math and physics constants, like π ("pi"), G (gravitational constant), etc. are typically predefined for you. This makes interactive work easier, however it is better practice to explicitly import these constants in your scripts.
  • Now we can use import to access some numpy functions. Let's start with some trigonometric functions. Type the following two lines, one at a time
    In [18]: from numpy import sin, cos, pi
    In [19]: print(cos(pi), cos(pi/2), sin(pi), sin(pi/2), sin(3*pi/2))

    Your output should look like the following.
    -1.0 6.12323399574e-17 1.22464679915e-16 1.0 -1.0

    The interpreter printed out cos(π/2) and sin(π) as very small numbers, quite close to zero. It is clear that the trig functions expect their arguments in radians.
The command
In [18]: from numpy import sin, cos, pi
specifies that we should use only the two functions sin() and cos() and the constant pi from the numpy package. But the numpy package includes a huge number of commands and to import them all we can type:
from numpy import *
In this line, the symbol * is not the multiplication symbol but a so-called wildcard that stands for all the functions, variables, etc. that are available from numpy.

Activity 3: To practice what we've learned, do the following:
  • We will now work on a program to calculate the distance between two satellites in circular orbit about a massive body M at some time t, given that they are initially aligned with each other and the sun. The expression for this can be found as follows: In time t, each satellite trajectory goes through an angle 2πt/T from its initial position, where T is the period of the orbit. So two satellites become separated by an angular distance

    (see the illustration below).
    By the cosine law, the distance between the planets is given by

    [Don't worry if you do not completely understand the formulas; for now, just take them as given formulas that depend on several variables.]
  • Now write a program that outputs the angular separation and other information. Ask the user to input the value of the mass in kilograms, the radii of the two orbits r1 and r2 in metres, and the time t in days.
  • To make the output more readable, it is a good idea to reduce the angle to a value in the range 0 to 2π radians or 0 to 360 degrees. Suppose the angular separation degrees is represented by the variable delta_theta_degrees. We can use the modulo operator % to do what we want:
    delta_theta_degrees = delta_theta_degrees%360
  • We can now proceed as follows:
    • Use input() to prompt the user for M, r1, and r2.
    • Calculate periods T1 and T2 from Kepler's third law, as we did in the previous example.
    • Calculate the angular distance delta_theta from the first formula.
    • Calculate the distance between the satellites delta_r using the second formula.
    • Output the results.
  • With these steps you should be able to write a fully functional program. Try to write it now!
  • Our solution can be found here: . The output from a typical session, with input parameters corresponding to Earth and Mars, can be found here: .

3. Defining your own functions

If you are going to evaluate the same expression or perform the same sequence of operations many times in a program, it is tedious and error-prone to keep retyping the same code. It is more efficient to create your own functions, something that Python lets you do easily. Functions make your code better structured, which means easier to write, understand and correct.

Let's start by creating a function to calculate radioactivity using the radioactive decay formula from Part 1 (see Part 1, Activity 7 and Activity 9):

To get the left hand side, you need three pieces of information: the current radioactivity, the elapsed time, and the half life. This means that we need to create a function with three arguments(current links are for Python 2 documentation). It's very helpful to imagine how you might use such a function in a program. For example, you could imagine a line in your script that looks like
new_activity = get_new_activity(old_activity, elapsed_time, half_life)

In this line, the new_activity receives the value of the function get_new_activity given the old_activity, the elapsed_time, and the half_life. So, now we know what we want our function to do, and have a basic design for it. How do we tell Python what we want? We create a function using Python's def command. Here's how we implement the function:
#function to calculate radioactivity given half-life formula
#r(t+delta_t) = r(t)*(1/2)**(delta_t/t_h):
#current_activity is r(t), elapsed_time is delta_t, half life is t_h
#elapsed_time and half_life need to be in same time units.
def get_new_activity( current_activity, elapsed_time, half_life):
    answer = current_activity*0.5**(elapsed_time/half_life)
    return answer

There are a few things to notice here.
  1. We started the code with a bunch of comments to make it clear what we are doing.
  2. The first non-comment line
    def get_new_activity( current_activity, elapsed_time, half_life):

    consists of the def keyword (command), which tells Python to expect a function definition, the function name get_new_activity, the function arguments (current_activity, elapsed_time, half_life), which are separated by commas and enclosed in parentheses, and the (very important) colon :.
    The following lines
    answer = current_activity*0.5**(elapsed_time/half_life)
    return answer
are indented(current links are for Python 2 documentation), that is, they are offset from the left margin by a few spaces. Please note:
  • The body of the function must always be indented; the number of spaces from the left margin is up to you, but the indentation must be consistent from line to line.
  • These indented lines which define the function are known as a code block.
    • Python uses indented whitespace to indicate code blocks, which we will see over and over again (for more info on blocks see the Python reference); this makes it different from many other programming languages.
  • The Spyder editor and shell will try to help you with indentation of code blocks code as you create scripts. Try to watch carefully what it does.

Now, what does the function do? The line beginning answer = calculates the radioactivity, and the line return answer outputs the numerical value of answer. Once the return line is reached, the interpreter exits the function.

To use the function, we simply call it like we called the sin() and cos() functions before. For example, the line
print get_new_activity( 4.0, 40.0, 20.)
will print the radioactivity given that the current activity is 4.0 (in arbitrary units), the elapsed time is 40.0 (in arbitrary units), and the half life is 20.0 (in arbitrary units, but the same as the units of the elapsed time). Can you predict the outcome of this call? [Hint: how many half lives have elapsed?]
One other point: because the interpreter carries out commands in the sequence they are written, the function must be defined before the function is used.

Activity 4: Let's try out our new function! Do the following:
  • Repeat Activity 9 of Part 1, but this time using the get_new_activity function defined above.
    • The old script for Activity 9 can be found here.
    • Modify this script by defining the get_new_activity function at the top of the script, and then print the values of the activity each decade for a few decades.
  • A sample solution can be found here and sample output can be found here.

Activity 5: More practice
  • Repeat Activities 1 and 2 above, but using defined functions.

You camn read more about defining your own functions and modules in the Introduction to Functions and Modules(current links are for Python 2 documentation).

4. Summary & Conclusion

  • One of the topics discussed in this tutorial was the use of the input() function. It allows you to prompt a user for input(current links are for Python 2 documentation).
    • This function is not the only function that allows user input but it is one of the most commonly used.
  • It was also mentioned again that commenting your code is very important. Commenting(current links are for Python 2 documentation) your code allows other people and yourself to understand code that cannot be immediately understood.
  • The last portion of this tutorial covered functions and modules(current links are for Python 2 documentation). Functions(current links are for Python 2 documentation) are a useful tool in organizing and reducing code. If you need to repeat a process multiple times, it is a good idea to make this process into a function. Modules(current links are for Python 2 documentation) are simply collections of functions and/or constants. It is very important to know how to make you own functions, so try it now.
(current links are for Python 2 documentation)

(current links are for Python 2 documentation)

This concludes Part 2 of the Tutorial. You can now move on to Tutorial Part 3.