In [1]:
import numpy as np

Exceptions

An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.

You've already seen some exceptions:

  • syntax errors
  • divide by 0

Many programs want to know about exceptions when they occur. For example, if the input to a program is a file path. If the user inputs an invalid or non-existent path, the program generates an exception. It may be desired to provide a response to the user in this case.

It may also be that programs will generate exceptions. This is a way of indicating that there is an error in the inputs provided. In general, this is the preferred style for dealing with invalid inputs or states inside a python function rather than having an error return.

Catching Exceptions

Python provides a way to detect when an exception occurs. This is done by the use of a block of code surrounded by a "try" and "except" statement.


In [2]:
def divide1(numerator, denominator):
    try:
      result = numerator/denominator
      print("result = %f" % result)
    except:
      print("You can't divide by 0!!")

In [3]:
divide1(1.0, 2)


result = 0.500000

In [4]:
divide1(1.0, 0)


You can't divide by 0!!

In [5]:
divide1("x", 2)


You can't divide by 0!!

Question: What do you do when you get an exception?

You can get information about exceptions.


In [6]:
#1/0

In [7]:
def divide2(numerator, denominator):
    try:
      result = numerator/denominator
      print("result = %f" % result)
    except (ZeroDivisionError, TypeError):
      print("Got an exception")

In [8]:
divide2(1, "x")


Got an exception

In [9]:
# Why doesn't this catch the exception?
# How do we fix it?
divide2("x", 2)


Got an exception

In [10]:
# Exceptions in file handling
def read_safely(path):
  error = None
  try:
    with open(path, "r") as fd:
      lines = fd.readlines()
      print ('\n'.join(lines()))
  except FileNotFoundError as err:
    print("File %s does not exist. Try again." % path)

In [11]:
read_safely("unknown.txt")


File unknown.txt does not exist. Try again.

In [12]:
# Handle division by 0 by using a small number
SMALL_NUMBER = 1e-3
def divide2(numerator, denominator):
    try:
      result = numerator/denominator
    except ZeroDivisionError:
      result = numerator/SMALL_NUMBER
    print("result = %f" % result)

In [13]:
divide2(1,0)


result = 1000.000000

Generating Exceptions

Why generate exceptions? (Don't I have enough unintentional errors?)


In [14]:
import pandas as pd
def func(df):
    """"
    :param pd.DataFrame df: should have a column named "hours"
    """
    if not "hours" in df.columns:
        raise ValueError("DataFrame should have a column named 'hours'.")

In [15]:
df = pd.DataFrame({'hours': range(10) })
func(df)

In [16]:
df = pd.DataFrame({'years': range(10) })
# Generates an exception
#func(df)

Class exercise

Choose one of the functions from the last exercise. Create two new functions:

  • The first function throws an exception if there is a negative argument.
  • The second function catches an exception if the modulo operator (%) throws an exception and attempts to correct it by coercing the argument to a positive integer.