Scientific Computing Using Python - PHYS:4905
Lecture #06 - Prof. Kaaret
Defining your own functions in Python
To define a function in Python, you use the def statement. The
following defines a function that calculates the nth root
of a number.
def nthroot(x, n) :
"""
nthroot returns the n-th root of x.
arguments: x, n
x is the value, n specifies which root
"""
y = x**(1.0/n)
return y
To call this function, use
nthroot(27.0, 3)
The 'declaration' of the function is the first line with the def statement.
The word after def
sets the name of the function, which in this case is nthroot. After
the name, there are parenthesis and inside the parathesis is a list
of arguments to the function. In this case, there are two
arguments x and
n. After
the parenthesis, there is a colon, just like in for and if statments.
After the declaration, it is customary to have a comment
block. For function definitions, Python will use the comment
if someone asks for help on the function by typing
help(nthroot). Probably the most important comments you can
make are the ones that describe your functions, since those will be
needed by anyone who uses the function (and not only people who look
inside at the code within your functions).
The body of the function is indented, just like in for and if statments.
At the end of the body, there is usually a return statement. For nthroot, we return
the calculated nth root. It is ok to not include a
return
statement. In that case, Python returns the value None.
To use the function, run the file that contains it. If you
edit the function, run the file again and the function will be
updated with the new definition. If you build your functions
into libraries and then import those libraries, you need to jump
through more hoops to update the function definitions.
Note that variables in the function are not the same as the
variables in your console or in the code calling the function.
Save the function above, run the file, and then do
nthroot(27.0, 3)
Now look at the Variable explorer pane. You don't see x, n, or y. This is
because those variables are local to the function. They are
defined only internally to the function. This helps protect
your variables from being changed inside the function, even if the
function uses an internal variable with the same name as one of
yours.
Functions can see variables used in the console window or the
calling code, as long as their names don't conflict with any
variables local to the function. However, this is bad (very
bad) programming practice. When you define functions you
should use the following guidelines:
- Pass data to a function only through it arguments.
- Do not modify the arguments of a function.
- Return any results with a return statement.
Python automatically makes a copy of any simple variables when
calling a function. Try changing nthroot so that the last two
lines are
x = x**(1.0/n)
return x
Run the file and then in the console window do
x = 3.14
nthroot(x, 3)
x
You'll see that the x
in the console window is unchanged, even though the function changed
x internally.
However, if you change the same two lines to
x[0] =
x[0]**(1.0/n)
return x[0]
and then in the console pane do
x = [3.14]
nthroot(x, 3)
x
You'll find that the value of x[0] has changed. Python does not
automatically copy lists or numpy arrays when passing them to
functions. Thus, it is possible to write functions that have
side effects, meaning they change in the variables in the calling
code (or console). Side effects are to be avoided because they
can create bugs that are a huge hassle to figure out. If you
need to modify an array inside of a function, make a copy of the
array with the copy function.
Augmented matrices in Python
Recall from the last lecture that we could write our system of
linear equation in matrix form and also as an augmented matrix with
no variables.
The augmented matrix form can be represented in Python as a matrix,
m = array([[1.0, 1.0,
27],[2, -1, 0]])
We can then write Python functions that carry out our three
elementary row operations.
- Row swap - exchange any two rows.
- Scalar multiplication - multiply any row by a non-zero
constant.
- Row addition - add a multiple of one row to another row.
The code is here:
def row_swap(m, i,
j) :
"""
Swap rows i and j of matrix m.
"""
mp = m.copy() # make a copy of the input matrix
t = mp[i,:].copy() # copy row i of mp, need to copy since
next line changes mp
mp[i,:] = mp[j,:] # copy row j of mp into row i
mp[j,:] = t # put the copy of the original row i into row
j
return mp
def scalar_mult(m, i, c) :
"""
Multiply row i of matrix m by c.
"""
mp = m.copy() # make a copy of the input matrix
mp[i,:] = c*mp[i,:] # multiply row i by c and put it back
into row i
return mp
def row_add(m, i, j, c) :
"""
Multiply row i of matrix m by c, add to row j, and
replace row j.
"""
mp = m.copy() # make a copy of the input matrix
mp[j,:] = c*mp[i,:]+mp[j,:] # multiply row i by c, add to
row j, and put sum back into row j
return mp
Copy these routines into Spyder and save the file as eros.py. Run or import the file.
Create an augmented matrix and try the functions in the file to
see if they work properly. Use the Variable explorer window
or print out the contents of your matrices to check. Let's
give it a go by doing the following commands and looking at the
contents of the matrix m after each.
from eros import *
m = array([[1.0, 1.0, 27],[2, -1, 0]])
m = row_swap(m, 0, 1)
m =
scalar_mult(m, 0, 2.0)
m = row_add(m, 0, 1,
2.0)
Assignment
The assignment for today's class is HW #6
and involves doing Gaussian elimination in Python. The
assignment is due at the beginning of the next class.