Python Exceptions

By

Published · Updated

Tagged: Python

Learn how exception handling works in Python with try, except, else, and finally blocks, how to raise your own errors, and how to define a custom exception.

It’s important to have a way to handle errors.

Python gives us exception handling. When something goes wrong, Python raises an exception. If nobody catches it, the program prints a traceback and stops.

try and except

You wrap the risky lines in a try block. If an error occurs, Python looks for an except block that matches that error:

try:
    result = 2 / 0
except ZeroDivisionError:
    print('Cannot divide by zero!')

Without the try, this program terminates:

result = 2 / 0
print(result)
Traceback (most recent call last):
  File "main.py", line 1, in <module>
    result = 2 / 0
             ~~^~~
ZeroDivisionError: division by zero

and the lines after the error will not be executed.

The specific error depends on the operation. Open a file that does not exist and you get a FileNotFoundError. Call input() and hit the end of input and you get an EOFError. File read() and readline() return an empty string at EOF. Divide by zero and you get a ZeroDivisionError. Convert a bad string like int('abc') and you get a ValueError. Use the wrong type, like '1' + 1, and you get a TypeError.

You can handle more than one error, each with its own block:

try:
    age = int('twelve')
except ValueError:
    print('That is not a number')
except TypeError:
    print('Give me a string or a number')

Python checks the except blocks from top to bottom and runs the first match. Then it skips the rest.

To catch all remaining exceptions you can use except without a type:

try:
    result = 2 / 0
except ZeroDivisionError:
    print('Cannot divide by zero!')
except:
    print('Something else went wrong')

That bare except: also catches KeyboardInterrupt and SystemExit. In practice use except Exception: (or except Exception as error:) so Ctrl-C still stops the program, and be as specific as you can.

as error gives you the exception object, so you can print the message or log it:

try:
    result = 2 / 0
except Exception as error:
    print(error)

else and finally

The else block runs only if no exception was raised:

try:
    age = int('8')
except ValueError:
    print('That is not a number')
else:
    print('Age is', age)

Use else for the success path. Keep try small: only the line that might fail. That way a bug in the success code does not get swallowed by your except.

A finally block runs in any case, whether an error occurred or not:

try:
    result = 2 / 0
except ZeroDivisionError:
    print('Cannot divide by zero!')
finally:
    result = 1

print(result) # 1

finally is the right place for cleanup: close a file, release a lock, disconnect. It runs even if you return from the try or except. For files, with open() does that cleanup for you, so you rarely write try/finally around open().

Raising exceptions

You can raise exceptions in your own functions, too, using the raise statement:

raise Exception('An error occurred!')

This raises a general exception, and you can intercept it using:

try:
    raise Exception('An error occurred!')
except Exception as error:
    print(error)

Raise an exception when the caller needs to know something went wrong. Return a normal value (None, False, 0) when a missing result is an expected outcome, not a failure.

You can also define your own exception class, extending from Exception:

class DogNotFoundException(Exception):
    pass

pass here means “nothing” and we must use it when we define a class without methods, or a function without code, too.

try:
    raise DogNotFoundException()
except DogNotFoundException:
    print('Dog not found!')

A custom exception is useful when the caller should treat your error differently from a generic Exception. DogNotFoundException is easier to catch than a message string on a plain Exception.

Want me to talk about your product? You can sponsor this site.

Related posts about python: