# | include: false
import numpy as np
import pandas as pd
256852) np.random.seed(
Functions (Good)
What to expect in this chapter
In this chapter, I will tie up some loose ends about functions like types of arguments and docstrings. I will also discuss exception handling so that you can better understand how to deal with errors. By the end of this chapter, you will know the difference between positional, keyword, and default arguments of functions. You can also write code that checks and handles potential problems.
1 Checks, balances, and contingencies
Users and programmers are not infallible, and you cannot think of everything that can go wrong. So, having checks, balances, and contingencies in your code is a good idea. So, let’s talk about pre-empting problems. This topic applies to more than just functions, but let’s start here.
There are two standard ways Python allows us to incorporate checks: assert
and try-except
.
1.1 assert
Python has a command called assert
that can check a condition and halt execution if necessary. It also gives the option of printing a message.
The basic syntax is as follows:
assert condition-to-check, message
assert
stops the flow if the condition fails. Here is an example.
assert x >= 0, "x is becoming negative!"
The program will run for as long as the condition is True
. If it fails, then an AssertationError
is raised, and the program stops running!
The following will run without a problem.
= 10
x assert x >= 0, "x is becoming negative!"
The following will throw an error and stop.
= -1
x assert x >= 0, "x is becoming negative!"
1.2 try-except
A technical name for things going wrong is exceptions. For example, division by zero will raise a ZeroDivisionError
. An exception left unhandled will halt the flow of the programme. However, if you are a control freak, Python offers an (absurdly) simple ‘try-except’ structure to catch and handle these exceptions yourself.
The try-except
syntax can also ensure that your programme can handle some situations beyond your control. For example, when I use Python to speak to the Canvas server, I use try-except
to handle situations when the server does not respond.
Let me show you how to use the try-except
flow control statement.
We can solicit a user response using the input()
function. Let’s say we do this and ask for a number, as shown in the snippet below.
=input("Give me a number and I will calculate its square.")
number=int(number)**2 # Convert English to number
squareprint(f'The square of {number} is {square}!')
This will work fine if the typecasting int(number)
makes sense. What if the input is not a number but something else like ‘hahaha’?
Let’s use the try-except
to get around this problem.
try:
=input("Give me a number and I will calculate its square.")
number=int(number)**2
squareprint(f'The square of {number} is {square}!')
except:
print(f"Oh oh! I cannot square {number}!")
Notice how I have enclosed (and protected) that part of the code that we think can potentially lead to trouble in the try
block. If something (anything) goes wrong, Python will ignore the error and run the code in the except
block.
You can have more control over how the expetions are handled with a try-except
block. However, we do not have to worry about that at this point.
1.3 A simple suggestion
When starting out with some code, it is always good for your code to signal to the outside world that it has finished certain milestones. A ‘soft’ way to do this is to include ‘print()’ statements here and there to let the outside world know what is happening in the innards of your program. Otherwise, you will stare at a blank cell, wondering what is happening.
2 Some loose ends
2.1 Positional, keyword and default arguments
In the past chapter, some of you may have noticed that I was (carelessly) switching between passing two styles of passing arguments to the function greeting()
. I wrote greeting('Super Man')
or greeting(name='Super Man')
. We need to talk a bit more about this so that you are not bewildered when you see other people’s code.
There are three ‘ways’ to pass a value to an argument. I will call them positional, keyword or default. To make this clearer, consider the following function.
def side_by_side(a, b, c=42):
return f'{a: 2d}|{b: 2d}|{c: 2d}'
Here are three ways I can use this function.
Positional
1, 2, 3) side_by_side(
Here, I am telling Python to assign 1, 2, 3
to a, b, c
using the positional order of the arguments.
Keywords
=3, b=1, a=2) side_by_side(c
Here, I explicitly specify the keyword to assign the values to each of a, b, c
. (No, the order does not matter)
Default
1, b=2) side_by_side(
Here, since c
is optional, I can choose not to specify it (of course, provided I want c
to be 1
).
Below are some examples of how you can combine these three styles. However, one style (keyword followed by positional) confuses Python and won’t work.
1, 2) # Two positional, 1 default
side_by_side(## ' 1| 2| 42'
1, 2, 3) # Three positional
side_by_side(## ' 1| 2| 3'
=1, b=2) # Two keyword, 1 default
side_by_side(a## ' 1| 2| 42'
=3, b=1, a=2) # Three keyword
side_by_side(c## ' 2| 1| 3'
1, c=3, b=2) # One positional, 2 keyword
side_by_side(## ' 1| 2| 3'
1, b=2) # One positional, 1 keyword, 1 default
side_by_side(## ' 1| 2| 42'
Let me reiterate that the following will not work because Python cannot unambiguously determine the position of 1
?
# Keywords cannot be followed
# by positional arguments
=2, 1) # Won't work. side_by_side(a
2.2 Docstrings
Python has a docstring feature that allows us to document what a function does inside the function. This documentation (i.e., the docstring) is displayed when we ask Python to show us the help info using help()
.
Here is a simple example.
def side_by_side(a, b, c=42):
'''
A test function to demonstrate how
positional, keyword and default arguments
work.
'''
return f'{a: 2d}|{b: 2d}|{c: 2d}'
A docstring needs to be sandwiched between a pair of '''
(or """
) and can span multiple lines.
Let’s see if it works by asking for help.
help(side_by_side)
Help on function side_by_side in module __main__:
side_by_side(a, b, c=42)
A test function to demonstrate how
positional, keyword and default arguments
work.
Docstrings can be used for writing multiline comments, but the practice is frowned upon by Puritans; so if you misuse it be ready for their ire!
2.3 Function are first-class citizens
Python functions are called first-class citizens because they have the same privileges as variables. This opens up useful possibilities for scientific programming because we can pass a function as an argument to another function!
Consider this:
def my_function(angle, trig_function):
return trig_function(angle)
# Let's use the function
/2, np.sin)
my_function(np.pi## 1.0
/2, np.cos)
my_function(np.pi## 6.123233995736766e-17
/2, lambda x: np.cos(2*x))
my_function(np.pi## -1.0
Note: When we pass a function as an argument, we do not include the parenthesis ()
.
2.4 More about unpacking
There is more to unpacking. For example, unpacking can make extracting information from lists and arrays a breeze. Here are some examples.
-
= [1, 2, 3] x, y, z x, y, z
(1, 2, 3)
-
= np.array([1, 2, 3]) x, y, z x, y, z
(1, 2, 3)
-
*y, z = np.array([1, 2, 3, 4, 5]) x, x, y, z
(1, [2, 3, 4], 5)
-
*_, y = [1, 2, 3, 4, 5] x, x, y
(1, 5)