Lab 8

Note: This is intentionally a shorter lab. If you get through all the exercises, you are encouraged to spend some time catching up on any exercises you missed in previous labs, or working on the assignment.

If you have any questions about or difficulties with any of the material covered in the course so far, ask your tutor for help during the lab.

Objectives#

The purpose of this week’s lab is to:

  • Explore python’s exceptions and exception handling mechanism.
  • Think about code quality - particularly with respect to function docstrings and variable names.

Exceptions#

Exceptions are python’s runtime error mechanism. The assert and raise statements allow the programmer to generate exceptions when error conditions occur, while the try statement allows us to implement an exception handler which will be activated if an exception occurs.

When a runtime error occurs, an exception (of a suitable type) is raised. If code in which the exception was raised is within the suite of a try statement that has an except clause for that type of exception, the suite of the except clause (that is, the exception handler) is executed; we say the exception has been caught, and execution continues with the next statement after the try statement. If this is not the case, and the exception was raised in the execution of a funciton, the exception is returned to point of the function call, and the same procedure repeated with the call treated as the origin of the exception. In this way, an exception travels up a call chain until it is either caught by an appropriate handler, or it reaches the top level, in which case the python interpreter stops execution and prints an error message.

Exercise 0#

Copy the following two function definitions to a file and run them:

def funA(a, i):
    try:
        return a[i] + i
    except TypeError:
        print("Caught TypeError in funA")
    except IndexError:
        print("Caught IndexError in funA")
    return 0

def funB(a, i):
    try:
	i = funA(a, i)
        return a[i]
    except TypeError:
        print("Caught TypeError in funB")
    except IndexError:
        print("Caught IndexError in funB")
    return 0

  • What happens if you call funB([3,2,1], 3)?
  • What happens if you call funB([3,2,1], 0)?
  • Can you find arguments to funB that cause the TypeError handler in funA to be executed?
  • Can you find arguments to funB that cause the TypeError handler in funB to be executed?
  • Can you find arguments to funB that cause an exception to be raised that is not caught by any of the exception handlers?

Exercise 1#

(a) Consider the following function (from the lecture on algorithm complexity):

def sorted_find(x, slist):
    if slist[-1] <= x:
        return slist[-1]
    lower = 0
    upper = len(slist) - 1
    while (upper - lower) > 1:
        middle = (lower + upper) // 2
        if slist[middle] <= x:
            lower = middle
        else:
            upper = middle
    return slist[lower]

The intent of the function is to return the greatest element that is less than or equal to x; it assumes that slist is a sorted sequence.

  • What are all the runtime errors that may be raised in this function?
  • Are there any further, unstated assumptions? That is, are there any cases in which the function will not raise an exception, but return an incorrect answer? If so, is there assertion that can be added to the function to ensure it raises an exception instead of failing silently?

(b) Pick a function that you have written as a solution to any exercise from any of the previous labs, and find the answers to the two questions above for that function.

Exercise 2#

Recall that python defines ordering of sequences so that a sequence S1 < S2 is True iff there is an index i such that S1[i] < S2[i] and S1[j] == S2[j] for indices j from 0 to i. This definition of ordering does not work for sets, because sets are not ordered, and not indexable (that is, we cannot take the i:th element of a set).

Let’s define an ordering on sets as follows: set A is less than set B if for every element a in A there exists some element b in B such that a < b is True. Recall that values of different types are not always comparable; if a and b are two values that cannot be compared (for example, if a is a string and b a number), a < b is considered to be False.

Write a function set_less_than(A, B), that takes two sets A and B and returns True if A is less than B according to the above definition, and False otherwise.

Examples

  • { 0.5, 1.5, 2.5 } is less than { 1, 2, 3 }
  • { 'a', 'b' } is less than { 'ab', 'bb' }
  • { 0, 'a', (0, 'a') } is less than { 1, 'c', (2, -3) }

Hint: The easiest way to determine if two elements are comparable is to try comparing them. If comparison is not possible, it will raise a TypeError.

Excercise 3#

We saw in the lecture that one purpose of a function docstring is to specify the type and purpose of each argument and the type of the return value. This can be done by including a statement such as:

:param param_name: purpose, type.
:return return type: purpose.

For each of the functions for exercise 2 and 3, go back and write a formal docstring for the function. Include a short description of what the function does and then for each parameter write a full description of its purpose and allowable types(s).

Then modify your functions to include assert statements to check that each of the input parameters are actually of the required type(s). If they are not, your assert statements should print a suitable and informative error message. You can test the type of a variable by writing:

type(my_variable)

Finally, for each variable that is used in either function, have a think about the name you have chosen for the variable. Does it clearly indicate the purpose of the variable? Is it too similar to another variable name? Ask the person sitting next to you to have a look at your code. Can they explain to you how it works? If they can’t easily see what’s going on, think about how you could re-organise it, rename variables or add comments to make it clearer.

bars search times arrow-up