Chapter 7. Exceptions#
Basic Exception Handling#
When we execute a program, some statements may cause exceptions (basically unhandled errors). One of the most obvious examples is division by zero. Consider this example:
x = 2
y = 0
print(x / y)
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[1], line 3
1 x = 2
2 y = 0
----> 3 print(x / y)
ZeroDivisionError: division by zero
If this happens during the execution of a script, the execution is stopped and the exception is shown to the user. But this doesn’t always seem ideal.
Consider writing calculator software. It would probably be bad, if the software just crashed every time we accidentally try to divide by zero. Instead we should show a meaningful error message to the user that explains what went wrong and ask him to try again. Let’s fix our example:
x = 2
y = 0
try:
print(x / y)
except ZeroDivisionError:
print("Uh-oh, you tried to divide by zero.")
print("Luckily, we caught the exception and prevented you from destroying the universe!")
Uh-oh, you tried to divide by zero.
Luckily, we caught the exception and prevented you from destroying the universe!
Much better! We caught the error and our script may proceed, e.g. by asking the user to provide us with another number.
The try
Statement and the except
Clause#
The cornerstones of exception handling are the try
statement and the except
clause.
The general form of the try ... except ...
construct looks as follows:
try:
# Some statements
except MyException:
# Some other statements
Here is how the try ... except ...
construct works in detail:
First, the try
clause (i.e. all the statements in the try
block) are executed.
If no exception happens during the execution of the try
clause, the except
clause is completely ignored and the code continues as usual:
try:
print("Code in the try clause")
print(3 / 2)
print("More code in the try clause")
except ZeroDivisionError:
print("An exception happened")
print("Continue execution")
Code in the try clause
1.5
More code in the try clause
Continue execution
Note that the string "An exception happened"
is never printed, because the try
clause does not raise an exception and so the except
clause is ignored.
If an exception does happen during the execution of the try
clause, the rest of the try
clause is skipped and the interpreter checks if the raised exception matches the exception after the except
keyword. If that is the case, the except
clause (i.e. the statements in the except
block) is executed. Afterwards the code continues as usual:
try:
print("Code in the try clause")
print(3 / 0)
print("More code in the try clause")
except ZeroDivisionError:
print("An exception happened")
print("Continue execution")
Code in the try clause
An exception happened
Continue execution
Note that the string "More code in the try clause"
is never printed, because an exception happens at the line print(3 / 0)
and so the rest of the try
clause after that line is skipped. Also note that because we caught the exception, the rest of the code continues as usual and so the line "Continue execution"
is still printed.
If the raised exception does not match the exception after the except
keyword, the except clause is ignored and the exception is still raised:
try:
print("Code in the try clause")
print(3 / 0)
print("More code in the try clause")
except ValueError:
print("An exception happened")
print("Continue execution")
Code in the try clause
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[5], line 3
1 try:
2 print("Code in the try clause")
----> 3 print(3 / 0)
4 print("More code in the try clause")
5 except ValueError:
ZeroDivisionError: division by zero
Note that the program crashes, so neither ""More code in the try clause"
nor "Continue execution"
are printed.
Handling Multiple Exceptions#
Consider this snippet:
user_value = "2"
user_value_int = int(user_value)
print(5 / user_value_int)
2.5
This may raise two exceptions - a ValueError
in case user_value
does not represent a valid integer and a ZeroDivisionError
in case the user value is 0
. We can handle both exceptions separately by writing multiple except
clauses:
user_value = "2"
try:
user_value_int = int(user_value)
print(5 / user_value_int)
except ValueError:
print(f"The user supplied a value of {user_value}, but the value must be an integer")
except ZeroDivisionError:
print("The user supplied a value of 0")
2.5
Let’s try this out:
user_value = "2a"
try:
user_value_int = int(user_value)
print(5 / user_value_int)
except ValueError:
print(f"The user supplied a value of {user_value}, but the value must be an integer")
except ZeroDivisionError:
print("The user supplied a value of 0")
The user supplied a value of 2a, but the value must be an integer
user_value = "0"
try:
user_value_int = int(user_value)
print(5 / user_value_int)
except ValueError:
print(f"The user supplied a value of {user_value}, but the value must be an integer")
except ZeroDivisionError:
print("The user supplied a value of 0")
The user supplied a value of 0
We can also handle multiple exceptions at the same time by specifying a tuple of exceptions after the except
keyword:
user_value = "2a"
try:
user_value_int = int(user_value)
print(5 / user_value_int)
except (ValueError, ZeroDivisionError):
print(f"The user supplied a value of {user_value}, which is invalid")
The user supplied a value of 2a, which is invalid
Working with Exception Objects#
It is often useful to bind the exception to a variable by specifying a variable after the exception name:
try:
print(1 / 0)
except ZeroDivisionError as e:
print(type(e))
<class 'ZeroDivisionError'>
As you can see, the exception e
is just a regular Python object of instance ZeroDivisionError
! This is not just theoretically interesting - it means that we can work with attributes of that object. For example we can convert the exception to a string and print it:
try:
print(1 / 0)
except ZeroDivisionError as e:
print(str(e))
division by zero
This is very useful if we want to see what originally caused an error. It is in fact common practice to print the exception object when encountering an exception - especially during debugging.
Raising Exceptions#
You can also raise exceptions yourself by utilizing the raise
statement:
raise ValueError("Bad value")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[13], line 1
----> 1 raise ValueError("Bad value")
ValueError: Bad value
This is useful if you want to indicate that a certain operation results in an error, which calling code may choose to handle.