08 Errors and Exceptions

Errors and Exceptions #

  • Errors: Issues in the syntax or runtime of a program that cause it to stop.
  • Exceptions: Errors detected during execution.

Handling errors/exceptions is essential!

Types of Errors #

Below you’ll find some types of errors. Please spot and fix them.

Syntax Errors #

print("Hello World

Runtime Errors #

x = 10 / 0

Difficult to handle beforehand.

Logical Errors #

Also called bugs. Here is a program to sum up to numbers 1 to 5 inclusively.

total = 0
for i in range(1, 5):  
    total += i
print(total)

The program prints 10 even though I’m expecting 15!

Exceptions and Handling them #

try-except block #

try:
    num = int(input("Enter a number: "))
    print(10 / num)
except ZeroDivisionError:
    print("You cannot divide by zero.")
except ValueError:
    print("Please enter a valid number.")

using except and finally #

except: will catch any exception, regardless of its type.

>>> try:
...  1 / 0
... except:
...  print("Error happened")
... 
Error happened
>>> try:
...  int("string here")
... except:
...  print("Error happened")
... 
Error happened
>>> 

Using plain except is not recommended for error handling, as it may catch exceptions you don’t intent to catch.

Using except (with the exception type specified) you can also catch the object representing the exception and then use it in the error handling block:

try:
    {}['Invalid key']
except Exception as e:
    type = e.__class__.__name__
    message = e.args[0]
    print(f"{type}: {message} occurred. Exiting...")

finally will be executed in any case, regardless if an exception happens in the try block or not:

try:
    file = open("example.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("File not found.")
finally:
    print("Execution complete.")

Exercise (10 min) #

Given the following hashtable:

capitals = {
    "Tokyo": 13960000,       # Japan
    "Ottawa": 1016000,       # Canada
    "Brasilia": 3055149,     # Brazil
    "Cairo": 10230350,       # Egypt
    "Helsinki": 658864       # Finland
}

city = input("What's the city? ")
population = capitals[city]
print(f"{city} has the population of {population}")

write code that lets the user ask for the city name for which it will display the population. Safely handle KeyError exception letting the user know that there is no data for given city.

Debugging basics #

  • Print statements
  • IDE tools (debugger)
  • Common errors to look for: indentation, incorrect data types, off-by-one errors.
def sum_list(numbers):
    result = 0
    for num in numbers:
        result += num
    return result

print(sum_list([1, 2, 3, "4"]))  # Debug this!

Clean Code Basics #

Some basic rules:

  1. No Dead Code
  2. No Useless Comments
  3. Good Formatting
  4. Use meaningful variable names.
  5. No Magic Constants -> extract then into named constants at the top of the file
  6. No Copy & Paste code -> DRY (Don’t Repeat Yourself), and/or use functions
  7. Make sure there is a Main (main()) Function

When refactoring, even before applying these rules, you must first understand the program. Only then you can proceed.

Run the program and capture its behaviour for all valid/required/important inputs. This will guide you showing you whether your refactored program still works as it used to.

Example: Refactor the following code:

import random
a = []
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
a.append(int(random.random() * 100))
c = 0
# last = None
# for new in a:
#     if last == new:
#         a.remove(new)
#     last = new
for i in range(0,10):  # here we loop
    if (a[i]%7== 0):
        c += 1
    if (a[i]%11==0):
        c += 1
    if (a[i] % 7 == 0):
        print(a[i], "is divisible by 7 or 11")
    if (a[i] % 11 == 0):
        print(a[i], "is divisible by 7 or 11")
print("found", c, "numbers divisible by 7 or 11")

Homework (graded) #

Exception Handling #

Replicate the UNIX wc command in Python:

  • Prompt the user for a file name.
  • Try to open the file and print the number of sentences (newlines), words and characters in the file.
  • Handle exception if the file is not found (FileNotFoundError).

Illustrative behaviour of wc:

$ wc /etc/passwd
  56   97 3258 /etc/passwd
$ wc does_not_exist
wc: does_not_exist: No such file or directory

Refactoring #

Refactor the following program:

import datetime

# Ask user for birth year
a = input("Enter your age: ")
a = int(a)

# Check if user is an adult
if a >= 18:
    print("You are an adult.")
if a < 18:
    print("You are not an adult.")

z = 100


# Check if user is a child 
if a < 13:
    print("You are a child.")
if a >= 13:
    print("You are not a child.")

# Check if user is a teenager 
if a >= 13 and a < 18:
    print("You are a teenager.")
if a < 13 or a >= 18:
    print("You are not a teenager.")

# Check if user is a senior citizen 
if a >= 65:
    print("You are a senior citizen.")
if a < 65:
    print("You are not a senior citizen.")

x = a * 2 - a

Make sure to follow the refactoring (clean code) rules highlighted above.