Exceptions
Python has an exception method that uses the try:, catch:, raise:, and finally: system. Older programming languages would return an error code from a function for a programmer to tell if something went wrong. However, that caused problems when the data type was expected to be something other than an integer, such as a class. The exception system makes this much more robust.
You'll notice that exceptions have already been raised to cause run-time errors. For example, if a programmer goes outside the bounds of a list, they'll get an error:
l = [1, 2, 3]
print(l[5])
This will cause the following error:
Traceback (most recent call last):
File "prog.py", line 2, in <module>
print(l[5])
IndexError: list index out of range
The IndexError exception is a class that can be "caught" to determine if this caused an error. What if we don't want the user of our program to see something like IndexError? Furthermore, these errors cause the python program to crash.
How about catching the error and providing a graceful way of recovering from an exception:
l = [1, 2, 3]
try:
print(l[5])
except:
print("Index 5 is not a good index.")
print("The program keeps on going since we caught the error.")
The code above will generate the following:
Index 5 is not a good index.
The program keeps on going since we caught the error.
Notice that we no longer get a runtime error, but rather an error that we can control.
What happens behind the scenes is that everything within a try block can be caught. With the except we gave here, we're catching EVERY single exception, including IndexError.
We can specify which errors we want:
l = (1, 2, 3)
try:
print(l[5])
except IndexError:
print("I tried to get an index that doesn't exist.")
except:
print("An exception occured that isn't an IndexError.")
In the code above, since we specified IndexError first, if that should happen to be caught, it would print I tried to get an index that doesn't exist. However, if another exception occured that is NOT IndexError, then the second exception would occur and print An exception occured that isn't an IndexError. If no exception occured, then nothing would be printed and the program will continue unabated.
l = (1, 2, 3)
try:
l[0] = 100
except IndexError:
print("Index is not a good index.")
except:
print("Another exception occured.")
print("The program keeps on going since we caught the error.")
The code above prints the following:
Another exception occured.
The program keeps on going since we caught the error.
Exception Cause
One problem wish the catch-all exception is that we don't know what caused it. However, we can catch Exception, which will catch every single type of exception. Then we can assign that exception class as a variable so we can query what caused it. For example,
l = (1, 2, 3)
try:
l[0] = 100
except IndexError:
print("Index is not a good index.")
except Exception as ex:
print("Another exception occured:", ex)
print("The program keeps on going since we caught the error.")
will print the following:
Another exception occured: 'tuple' object does not support item assignment
The program keeps on going since we caught the error.
We can tell that the reason this caused an error was because a 'tuple' does not support item assignment. Again, every exception inherits Exception, so everything is an Exception.
Creating Exceptions
Programmers can create their own exceptions by creating a class that inherits Exception:
class MyError(Exception):
def __init__(self):
self.what = "Hello"
def __str__(self):
return self.what
l = (1, 2, 3)
try:
raise MyError
except IndexError:
print("Index is not a good index.")
except MyError as me:
print("MyError occured:", me)
except Exception as ex:
print("Another exception occured:", ex)
print("The program keeps on going since we caught the error.")
Notice that we're raising MyError inside of try, raise is a way to cause an exception. This program will print the following:
MyError occured: Hello
The program keeps on going since we caught the error.
Where does "Hello" from above come from? Well, notice we're printing ex, which is converted to a string through __str__, which simply returns self.what, which is assigned to "Hello" by __init__.
Notice that this is the way Exception works as well. If we caught MyError as a generic exception, we would get the following:
class MyError(Exception):
def __init__(self):
self.what = "Hello"
def __str__(self):
return self.what
l = (1, 2, 3)
try:
raise MyError
except IndexError:
print("Index is not a good index.")
except Exception as ex:
print("Another exception occured:", ex)
print("The program keeps on going since we caught the error.")
Another exception occured: Hello
The program keeps on going since we caught the error.
Therefore, ex converts itself into a string when we use print().
References
https://docs.python.org/3/tutorial/errors.html
Stephen Marz (20190610)