le3_functions_and_exceptions
le3_functions_and_exceptions
When working with for and while loops, there may be cases where the programmer would like to stop the loop before the loop's condition evaluates to false (in the case of while loops) or before the elements in
the iterable are exhausted (in the case of for loops). To do this, we use the break statement
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:
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.
The set of parentheses contain the function's parameter list - a comma-separated sequence of parameters representing the data that the function needs to perform its task. Function square above only has
one parameter named number - the value to be squared.
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.
In Python, None is used to represent the absence of a value. It evaluates to False in conditional statements.
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 [ ]: 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.
In [ ]: # A variable created outside of a function or class is global and
# can be used by anyone:
var_global = 300
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 inside a function
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:
display(dir(math)) # List out all the functions/classes available within the module
['__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']
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.radians))
cos(x, /)
Return the cosine of x (measured in radians).
None
Help on built-in function radians in module math:
radians(x, /)
Convert angle x from degrees to radians.
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))
Out[ ]: 0.5000000000000001