Error handling and exceptions in Python

Errors can typically occur in the following scenarios:

  • A file or resource that is referred to does not exist
  • A network connection fails
  • A module import error occurs

In these scenarios you would want to allow for error handling, so that you can detect errors and respond appropriately.

Difference between errors and exceptions.

Python distinguises between errors and exceptions.

  • An error typically indicates a situration where something goes wrong before the execution of the program begins.
  • An exception arises during the execution of the program

In contrast to a error, when an exception occurs, the program doesn’t necessarily stop immediately. Instead, Python provides a way to handle the exception, allowing you to potentially recover from it, or at least, to handle it gracefully before the program stops.

However if you do not implement exception handling, the program will stop immediately when an exception occurs, similar to a error.

The Exception hierarchy

Errors and Exceptions are objects in Python and there is no real syntactic distinction between the two since all errors and exceptions in herit from a base exception class.

The root class is BaseException which all errors and exeptions extend as subclasses as demonstrated by this diagram:

Exception syntax

Difference between raise and except:

  • raise is used to explicitly trigger an exception - it means that you are signalling that an exception condition has occured in your program.
  • except is used in conjunction with tryblocks to catch and handle exceptions. Here you are saying “I think this might cause an exception, so let’s be prepared to handle it”.

Exaple of raise:

x = -10
if x < 0:
    raise ValueError("The value should not be negative!")

Example of except:

try:
    result = 10 / 0
except ZeroDivisionError:
    print("You can't divide by zero!")

Scaffolding exception handling

There is a general procedure for handling exceptions denoted by certain keywords:

  • try
    • The process you want to run
  • except
    • The errors that could occur. You can have multiple except clauses for different exceptions
  • else
    • Some code you want to run after each of the except clauses have run
    • It must be written after the except clauses
    • It runs if and only if no exceptions were raised
    • If try succeeds or an exception is thrown, else will not run
  • finally
    • What you want to run at the end of the try, except, else sequence
try
    run_calculation(7)
except ZeroDivisionError:
    print('something')
except IndexError:
    print('something')
except FileNotFoundError:
    print('something')
except Exception:
    print('something')
else
    # Do something after the exception blocks
finally
    # Do concluding action

Custom exceptions

You can create your own custom exceptions by creating a class that inherits from the Exception class.

class CustomError(Exception):
    pass

try:
    raise CustomError("This is a custom exception!")
except CustomError as e:
    print(f"Caught an exception: {e}")