Functions and Exceptions
Contents
Functions and Exceptions¶
We have used functions that are already available in Python, but never defineda function on our own. A function is a reusable piece of code that can accept input parameters, also known as âargumentsâ.The purpose of defining a function is to give a name to a computational process that may be applied multiple times. There are many situations in computing that require repeated computation. For example, it is often the case that we want to perform the same manipulation on every value in a column of a table.
Defining a Function¶
The definition of the double
function below simply doubles a number.
# Our first function definition
def double(x):
""" Double x """
return 2 * x
Functions begin with the def
keyword, then the function name, arguments in parentheses, and then a colon (:
). The code executed by the function is defined by indentation. The output or âreturnâ value of the function is specified using the return
keyword. Here is a breakdown of the parts (the syntax) of this small function:
When we run the cell above, no particular number is doubled, and the code inside the body of double
is not yet evaluated. In this respect, our function is analogous to a recipe. Each time we follow the instructions in a recipe, we need to start with ingredients. Each time we want to use our function to double a number, we need to specify a number.
We can call double
in exactly the same way we have called other functions. Each time we do that, the code in the body is executed, with the value of the argument given the name x
.
double(16)
32
double(64/8)
16.0
The two expressions above are both call expressions
. In the second one, the value of the expression 64/8
is computed and then passed as the argument named x
to the double
function. Each call expresson results in the body of double
being executed, but with a different value of x
.
The body of double
has only a single line:
return 2*x
Executing this return
statement completes execution of the double
functionâs body and computes the value of the call expression.
The argument to double
can be any expression, as long as its value is a number. For example, it can be a name. The double
function does not know or care how its argument is computed or stored; its only job is to execute its own body using the values of the arguments passed to it.
any_name = 64
double(any_name)
128
Local Variables¶
Variables that are defined inside a function, including arguments like double's x
, have only a fleeting existence. It is local, which means that it only exists inside the function. They are defined only while the function is being called, and they are only accessible inside the body of the function. We canât refer to x
outside the body of double
. The technical terminology is that x
has *local scope*
.
Letâs look at another example
def concat(str1, str2):
string = str1 + str2
return string
concat('I love ', 'Python')
'I love Python'
string
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-9-edbf08a562d5> in <module>
----> 1 string
NameError: name 'string' is not defined
Docstrings¶
Though double
and concat
are relatively easy to understand, many functions perform complicated tasks and are difficult to use without proper explanation. Therefore, a well-composed function has a name that evokes its behavior, as well as documentation. In Python, this is called a docstring â a description of its behavior and expectations about its arguments. The docstring can also show example calls to the function, where the call is preceded by >>>
.
A docstring can be any string, as long as it is the first thing in a functionâs body. Docstrings are typically defined using triple quotation marks at the start and end, which allows a string to span multiple lines. The first line is conventionally a complete but short description of the function, while following lines provide further guidance to future users of the function.
Here is a definition of a function called percent
that takes two arguments. The definition includes a docstring.
def percent(x, total):
"""Convert x to a percentage of total.
More precisely, this function divides x by total,
multiplies the result by 100, and rounds the result
to two decimal places.
>>> percent(4, 16)
25.0
>>> percent(1, 6)
16.67
"""
return round((x/total)*100, 2)
percent(45,200)
22.5
Null Return Type¶
If you do not specify a return value, the function returns None
when it terminates:
def test_function(x):
x + 1 # no return!
if x == 999:
return
print(test_function(0))
None
Optional & Required Arguments¶
Sometimes it is convenient to have default values for some arguments in a function. Because they have default values, these arguments are optional, and are hence called âoptional argumentsâ. For example:
def repeat_string(string, n=2):
return string * n
repeat_string('Hello! ')
'Hello! Hello! '
repeat_string('Hello! ', 6)
'Hello! Hello! Hello! Hello! Hello! Hello! '
Ideally, the default value for optional arguments should be carefully chosen. In the function above, the idea of ârepeatingâ something makes me think of having 2 copies, so n=2
feels like a reasonable default.
Required and Optional arguments
You can have any number of required arguments and any number of optional arguments. All the optional arguments must come after the required arguments. The required arguments are mapped by the order they appear. The optional arguments can be specified out of order when using the function.
>>> def example(a, b, c="DEFAULT", d="DEFAULT"):
print(a, b, c, d)
>>> example(1, 2, 3, 4)
1 2 3 4
Specifying only one of the optional arguments, by keyword:
>>> example(1, 2, c=3)
1 2 3 DEFAULT
Specifying keyword arguments before non-keyword arguments will give an error
>>> example(a=2, 1)
File "<ipython-input-23-a37b920e8205>", line 1
example(a=2, 1)
^
SyntaxError: positional argument follows keyword argument
Multiple Return Values¶
In many programming languages, functions can only return one object. That is technically true in Python too, but there is a âworkaroundâ, which is to return a tuple.
def sum_and_product(x, y):
return (x + y, x * y)
sum_and_product(5, 6)
(11, 30)
The parentheses can be omitted (and often are), and a tuple
is implicitly returned as defined by the use of the comma:
def sum_and_product(x, y):
return x + y, x * y
sum_and_product(5, 6)
(11, 30)
It is common to immediately unpack a returned tuple into separate variables, so it really feels like the function is returning multiple values:
summed, product = sum_and_product(5, 6)
summed
11
product
30
Functions with Arbitrary Number of Arguments¶
You can also call/define functions that accept an arbitrary number of positional or keyword arguments using *args
and **kwargs
.
def add(*args):
print(args)
return sum(args)
add(1, 2, 3, 4, 5, 6)
(1, 2, 3, 4, 5, 6)
21
def add(**kwargs):
print(kwargs)
return sum(kwargs.values())
add(a=3, b=4, c=5)
{'a': 3, 'b': 4, 'c': 5}
12
Functions as a Data Type¶
In Python, functions are actually a data type:
def doubles(x):
return x + x
type(doubles)
function
print(doubles)
<function doubles at 0x7fbf81f83310>
This means you can pass functions as arguments into other functions.
def say_hi(default_greet = 'Hi'):
return default_greet
def greet_person(function, name):
return function() + f' {name}'
greet_person(say_hi, 'Bruce')
'Hi Bruce'
So what happened above?
function()
becomessay_hi()
Anonymous Functions¶
There are two ways to define functions in Python. The way weâve beenusing up until now:
def doubles(x):
return x + x
Or by using the lambda
keyword:
lambda_doubles = lambda x: x + x
type(lambda_doubles)
function
lambda_doubles(8)
16
The two approaches above are identical. The one with lambda
is called an anonymous function. Anonymous functions can only take up one line of code, so they arenât appropriate in most cases, but can be useful for smaller things.
Exceptions¶
So far we have made programs that ask the user to enter a string, and we also know how to convert that to an integer.
text = input("Enter something: ")
number = int(text)
print("Your number doubled:", number*2)
Enter something: 8
Your number doubled: 16
This code seems to work fine when a number is given as an input but doesnât work when the given input is a non-numeric type like a string or list
text = input("Enter something: ")
number = int(text)
print("Your number doubled:", number*2)
Enter something: eight
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-2-339ef37addcc> in <module>
1 text = input("Enter something: ")
----> 2 number = int(text)
3 print("Your number doubled:", number*2)
ValueError: invalid literal for int() with base 10: 'eight'
In this section we will look at how to fix that?
What are exceptions?¶
In the previous example we got a ValueError (first line of our error message). ValueError is an example of a exception which gets raised whenever a program execution hits an unexpected condition. The interactive prompt will display an error message and keep going.
There are different types of exceptions like..
# index error
mylist = [1, 2, 5, 7]
mylist[4]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-6-4911d298beb3> in <module>
1 # index error
2 mylist = [1, 2, 5, 7]
----> 3 mylist[4]
IndexError: list index out of range
# type error
int(mylist)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-53bfeee10670> in <module>
1 # type error
----> 2 int(mylist)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
Some common error types include:
SyntaxError
: Python canât parse programNameError
: local or global name not foundAttributeError
: attribute reference fails âTypeError
: operand doesnât have correct typeValueError
: operand type okay, but value is illegalIOError
: IO system reports malfunction (e.g. file not found)
If an exception occurs, the program will stop and we get an error message.
Catching exceptions¶
If we need to try to do something and see if we get an exception, we
can use try
and except
. This is also known as catching the
exception.
try:
a = int(input("Give me a number:"))
b = int(input("Give me another number:"))
print(a/b)
print ("Okay")
except:
print("Bug in user input.")
print("Outside")
Give me a number:4
Give me another number:5
0.8
Okay
Outside
The except part doesnât run if the try part succeeds.
try:
a = int(input("Give me a number:"))
b = int(input("Give me another number:"))
print(a/b)
print ("Okay")
except:
print("Bug in user input.")
print("Outside")
Give me a number:lol
Bug in user input.
Outside
Python tries to execute the code in the try
block. If an error is encountered, we âcatchâ this in the except
block (also called try
/catch
in other languages).