python functions and scripts
syntax similar to if/else statements and for loops:
colon to end the function name/argument names,
and indentation of the function body.
def function_name(arguments):
"""docstring here: to explain what the function does"""
command indented
indented command
return value # returns None if no return statement
arguments can be named, can have default values:
def foo(arg1, arg2=0):
"""
Return arg1 -1 + arg2.
arg2 is optional, 0 by default.
good practice: include examples.
Examples:
>>> foo(5)
4
>>> foo(5,8)
12
>>> foo(5, arg2=2)
6
"""
assert type(arg1)==int, "error message: here arg1 should be an integer"
res = arg1 - 1 + arg2
return res
example:
def startswithi(str):
"""Return True if the input string starts with 'i', False otherwise.
Require that the "re" was imported beforehand.
note:
- the double and single quotes inside my tripe double-quoted docstring
Example:
>>> startswithi("hohoho")
False
"""
return(bool(re.search(r'^i', str)))
help(startswithi) # or ?startswithi in interactive session
print(startswithi("iamcecile"))
print(startswithi("hohoho"))
key principle: break problem down into small parts
- write functions
- if you do some “copy-paste” of your code: you need to write a function
- functions make your code easier to debug, easier to read
- use meaningful names for functions and for variables
example where we re-use our functions:
def fahr_to_kelvin(temp):
return ((temp - 32) * (5/9)) + 273.15
print('freezing point of water:', fahr_to_kelvin(32))
print('boiling point of water:', fahr_to_kelvin(212))
def kelvin_to_celsius(temp_k):
return temp_k - 273.15
print('absolute zero in Celsius:', kelvin_to_celsius(0.0))
def fahr_to_celsius(temp_f):
temp_k = fahr_to_kelvin(temp_f)
result = kelvin_to_celsius(temp_k)
return result
print('freezing point of water in Celsius:', fahr_to_celsius(32.0))
example to break down:
import numpy
import glob
import matplotlib
filenames = glob.glob('inflammation*.csv')
def analyze_all():
for f in filenames[:3]:
print(f)
analyze(f)
detect_problems(f)
def analyze(filename):
data = numpy.loadtxt(fname=filename, delimiter=',')
# commands to make the figure for one data file
def detect_problems(filename):
data = numpy.loadtxt(fname=filename, delimiter=',')
if (numpy.max(data, axis=0)[0] == 0 and
numpy.max(data, axis=0)[20] == 20):
print('Suspicious looking maxima!')
elif numpy.sum(numpy.min(data, axis=0)) == 0:
print('Minima add up to zero!')
else:
print('Seems OK!')
analyze_all()
python scripts
-
to use functions in a script
binomial.pyinside a python session as a module:import binomialthen use function
fooasbinomial.foo(),help(binomial), etc. alternatively, do:from binomial import *and then use function
fooas justfoo(with no need to type the file name first). -
If your script in not in the directory that your python session is in, add the script path to the list of paths that python knows about:
import systhensys.path.append("path/to/script").- special predefined variables: try
binomial.__name__andbinomial.__file__after importing the module - documentation for the module: add a docstring at the beginning, after the shebang line if you have one
-
to reload a module that has already been loaded in same python session:
import binomial import importlib # then further edits made to 'binomial.py' importlib.reload(binomial)
- special predefined variables: try
-
to run the script from the command line, first put what should be run inside a test:
if __name__ == '__main__': command1 # things to do if script called command2 # from the command line.then do
python binomial.pyto run the script. -
to run it with
./binomial.pyor simplybinomial.py, change the file permission to let you execute the file, e.g. withchmod u+x, and add the “shebang” at the beginning of the file:#!/usr/bin/env pythonnote:
envis a shell command.env pythonfind the path to the python program and runs it. The shebang line has to give an absolute path, and the path toenvis quasi-always/usr/bin/env: so this line makes your script portable to other users who might not have the same path to python as you.
test python code automatically
- test each function in your code, run all tests each time your change your code.
- big thing: new features often break older functions.
- each time you fix a bug: add a new test, for the situation in which the bug appeared
- there are many modules for automatic testing; one is
doctest.
first, add examples the docstring of each function:
def choose(n, k):
"""returns the binomial coefficient.
Examples:
>>> choose(5,3)
10
"""
# function body that does the calculations
second, call doctest.testmod(), for example when the
file is run as a script:
if __name__ == '__main__':
import doctest
doctest.testmod()
script arguments
script name and arguments are captured in the list sys.argv
after you import sys, but use the
argparse library instead
to do things well, more easily, and with documentation.
#!/usr/bin/env python
"""module with very cool functions to say 'hi'"""
import argparse
# use an Argument Parser object to handle script arguments
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int, help="number of times to say hi")
parser.add_argument("-l", "--long", action="store_true", help="whether to say 'hi' the long way")
parser.add_argument("-g", "--greetings", type=str, help="greeting message, like a name")
parser.add_argument("--test", action="store_true", help="tests the module and quits")
args = parser.parse_args()
hi = "Howdy" if args.long else "Hi"
# test argument problems early:
if not args.test and __name__ == '__main__':
if args.n<0:
raise Exception("argument -n must be 0 or positive")
# no error if file imported as module
def print_greetings(extra_greetings, n=args.n):
"""
print individualized greeting. example:
>>> print_greetings("have a good day", 0)
have a good day, you.
"""
s = ""
for i in range(0,n):
s += hi + ", "
if extra_greetings:
s += extra_greetings + ", "
s += args.greetings if args.greetings else "you"
s += "."
print(s)
def runTests():
print("testing the module...")
if args.n:
print("ignoring n for testing purposes")
import doctest
doctest.testmod()
print("done with tests.")
if __name__ == '__main__':
if args.test:
runTests()
else:
print_greetings("")
we could save the example above in a file example.py and use it in various ways
from the shell:
./example.py --help
./example.py --test
./example.py -n=1 --long -g=cecile
./example.py -n 1 --long -g cecile
or within python:
import example
help(example)
example.print_greetings("happy halloween", 3)