Le3 Functions and Exceptions Part 1
Le3 Functions and Exceptions Part 1
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
Problem
Write a program that generates a random number between 0-100 and asks the user guess
the number. Each time telling the user whether their guess was greater than or less than the
generated number or whether it's correct. If the user makes 8 wrong guesses, he loses and
the game ends.
In some cases we might want to skip the execution of our loop's body for certain values, for
such cases we use the continue statement. Anytime a continue statement is
encountered in a loop, Python moves to the next iteration of the loop without executing any
code left in the body of the loop:
1
2
3
4
5
6
8
9
10
Functions
Functions are a convenient way to divide our code into useful blocks, allowing us to order
our code, make it more readable, and reuse it.
We have seen several builtin function in this course already: print() , len() ,
range() , etc. Packaging code as a function allows us to execute it from various locations
in our program just by calling the function, rather than duplicating the block of code in
multiple places in our code.
Defining a function
We can also define our own functions in Python. Here is an example of a function:
In [ ]: def square(number):
"""Calculate the square of number.
Args:
number: The value to be squared.
Returns:
result: The square of argument `number`.
"""
result = number ** 2
return result
25
81
A function definition
The function name is an identifier, and so all the naming conventions discussed previously
about identifiers applies.
When a function finishes executing, it returns control to its caller - that is, the line of code
that called the function. If a return statement is defined in the function, the value the
statement returns is passed on to the caller. If no return statement is defined in the
function, a value of None is returned. In the case of the square function, the square of
the argument number is returned by the function.
To call a function properly, it is important to pass in the arguments in the order they appear
in the function definition. For example, to calculate the volume of a cylinder with
radius=2.4cm and height=10cm. The dimensions of the cylinder must be passed in the
specific order the function expects: volume_cylinder(2.4, 10) . Passing the
arguments in a different order will produce a different result. volume_cylinder(10,
2.4) will produce the volume of a cylinder with radius=10 and height=2.4.
To avoid this pitfall, it is also possible to use the argument names in calling statement of the
function: volume_cylinder(radius=2.4, height=10) will yield the same result as
volume_cylinder(height=10, radius=2.4) because we are specifying which
value is the height and which is the radius in our function calls.
A function can also take an arbitrary number of arguments. For example, we can modify
function find_maximum to take as many arguments as the user wants. This is typically
useful when the order of the arguments does not matter:
In [ ]: def find_maximum(*numbers):
"""Return the highest number between number_1, number_2, number_3."""
result = None
for num in numbers:
if (result is None) or (num > result):
result = num
return result
In [ ]:
In [ ]: import random
help(random.randint)
Variable scopes
It is important to discuss the reachability of python variables. A variable is only available
from within the region it is created. Variables created inside functions belong to the local
scope of that function, and can only be used inside that function. Such functions are
sometimes referred to as local variables.
In [ ]: def our_function():
var_inside_our_func = 50
print(var_inside_our_func) # This will print out 50
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
/tmp/ipykernel_6222/2394188547.py in <module>
3 print(var_inside_our_func) # This will print out 50
4
----> 5 print(var_inside_out_func) # This will raise a NameError exception
In the example above, variable var_inside_out_func is only available from within the
function our_func . Trying to reference the variable outside the function will cause a
NameError exception.
Global Variable
A variable created in the main body of the Python code is referred to as a global variable.
Global variables are available from within any scope, global and local.
def new_function():
print(f"From inside function `new_function`: {var_global}")
Important notice
If you attempt to modify the var_global variable from within a function or a class, Python will
treat it as a different variable from the var_global defined at the top of the piece of
code:
def newest_function():
var_global = 150 # Just a local variable that belongs to this function
newest_function()
If you need to modify the global variable from within the scope of a function, you must use
the global keyword to make this possible
def newest_function():
global var_global
var_global = 150 # Modifies the value of the global variable from insid
newest_function()
Syntax Errors
Syntax errors occur when an invalid line code is present in code being executed by the
interpreter.
In [ ]: score = 70
if score >= 70 print("A")
Exceptions
Even if the statement or expression is syntactically correct, it may cause an error when an
attempt is made to execute it. Errors detected during execution are called exceptions. There
are many issues that could lead to exceptions in a program's execution. Most common is if
the program is used in an unexpected way. Let's say we have a function that accepts two
numbers a and b and returns the result of dividing a by b :
In [ ]: divide(a=90, b=0)
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
/tmp/ipykernel_11536/2015877406.py in <module>
----> 1 divide(a=90, b=0)
/tmp/ipykernel_11536/3979253694.py in divide(a, b)
1 def divide(*, a, b):
2 "Divide a by b."
----> 3 return a / b
If we attempt to pass an invalid value to the argument of the function, say a string, we will
also get an exception:
In [ ]: divide(a='ab', b=2)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_11536/1540736037.py in <module>
----> 1 divide(a='ab', b=2)
/tmp/ipykernel_11536/3979253694.py in divide(a, b)
1 def divide(*, a, b):
2 "Divide a by b."
----> 3 return a / b
The last line of the error message will always give an explanation of what went wrong.
Exceptions come in different types, and the type is printed as part of the error message: the
types in the examples above are ZeroDivisionError and TypeError .
Handling Exceptions
We can anticipate common exceptions that may occur when users are trying to use our
program when we are designing the program. We could use try-catch blocks to catch the
common exceptions our divide function may encounter while in use:
First, the try clause (the statement(s) between the try and except keywords) is
exected.
If no exception occurs, the except clause is skipped and exection of hte try statement is
finished.
If an exception occurs during execution of the try clause, the rest of the clause is
skipped. Then, if its type matches the exception named after the except keyword, the
except clause is executed, and then execution continues after the try/except block.
If an exception occurs which does not match the exception named in the except clause,
it is passed on to outer try statements; if no handler is an unhandled exception and
execution stops with a message as shown above.
Another example
Assuming we have an input statement that requires a user to enter a number, we could add
an exception block to keep requesting an input from the user until a number is entered:
In [ ]: while True:
try:
x = int(input("Please enter a number: "))
break
except ValueError:
print("You are required to enter a number. Please try again...")
print(f"Finally, you entered {x}")
We can re-use a module in another module by using the import statement. For example,
we have used functions in the math module that come with every python installation by
calling import math .
After import the math module, we might want to find out the functions that are available
within the module. To find out, we can use the dir() function:
['__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'acos',
'acosh',
'asin',
'asinh',
'atan',
'atan2',
'atanh',
'ceil',
'comb',
'copysign',
'cos',
'cosh',
'degrees',
'dist',
'e',
'erf',
'erfc',
'exp',
'expm1',
'fabs',
'factorial',
'floor',
'fmod',
'frexp',
'fsum',
'gamma',
'gcd',
'hypot',
'inf',
'isclose',
'isfinite',
'isinf',
'isnan',
'isqrt',
'lcm',
'ldexp',
'lgamma',
'log',
'log10',
'log1p',
'log2',
'modf',
'nan',
'nextafter',
'perm',
'pi',
'pow',
'prod',
'radians',
'remainder',
'sin',
'sinh',
'sqrt',
'tan',
'tanh',
'tau',
'trunc',
'ulp']
file:///home/gibson/Documents/Untitled Folder/ece272/le3_functions_and_exceptions_part_1.html 9/10
22/06/2023, 22:11 le3_functions_and_exceptions_part_1
You can ignore the function/classes that begin with and end with underscores as they have
a special meaning in Python.
We can get instructions on how to use the functions and classes within an imported module
by calling the builtin help() function:
In [ ]: import math
display(help(math.cos))
print()
display(help(math.degrees))
cos(x, /)
Return the cosine of x (measured in radians).
None
Help on built-in function degrees in module math:
degrees(x, /)
Convert angle x from radians to degrees.
None
From using the help function, we can see that we can find the cosine of an angle x which is
in radians by calling the math.cos function.
In [ ]: math.cos(math.radians(60))
0.5000000000000001
Out[ ]: