6. Errors and catching them#

There are two types of errors in Python. A few really bad errors are segfaults. These are almost always something triggered via the C interface (such as by using ctypes), and are not due to problems in your Python code. Most errors in Python are part of the language, called Exceptions.

An Exception is just a special control flow feature for things that are “exceptional”; often errors, but they are used for other things. In fact, internally, loops end by triggering a special exception!

Exceptions “bubble up” through the stack to the outermost scope. If you catch an exception before it reaches the top, you can handle it. If you don’t, then it shows up on the screen or in your logs as a traceback.

# Uncomment this to see an exception:
# 1 / 0
try:
    1 / 0
except ZeroDivisionError:
    pass

Exceptions use inheritance to form a tree structure, so you can be as tight or as loose as needed in catching them. Let’s see the parents of Zero Division error:

ZeroDivisionError.__mro__
(ZeroDivisionError, ArithmeticError, Exception, BaseException, object)

You could catch any of these instead:

try:
    1 / 0
except ArithmeticError as e:
    print(repr(e))
ZeroDivisionError('division by zero')

Always catch the most narrow exception you can! Never try to catch a really broad class or all exceptions, because things like running out of memory, exit signals, and more are exceptions too, and you don’t want to catch those if you didn’t mean to!

Here’s a basic example of making your own:

class MyNewException(RuntimeError):
    pass


try:
    raise MyNewException()
except MyNewException:
    pass

There can be as many except blocks as you need, there’s an else block if you want something to run only if nothing was caught, and there’s also a finally block, which will always run, even if the exception is caught:

try:
    1 / 0
except ArithmeticError:
    print("Caught the exception!")
finally:
    print("I can run cleanup, regardless of what happens above!")
Caught the exception!
I can run cleanup, regardless of what happens above!

Where would you want something like this? How about closing a file!

try:
    f = open(...)
    # do stuff with f that might throw an exception (basically anything)
finally:
    f.close()

This way, if an exception is thrown, the file still gets nicely closed. In fact, this is so important, we’ll see a feature built around it soon!