You’re all familiar with exception handling using try/except:
>>> x = [1, 2, 3, 4, 5]
>>> x[10]
Traceback (most recent call last):
...
IndexError: list index out of range
You can catch all exceptions quite easily:
>>> try:
... y = x[10]
... except:
... y = None
but this is considered bad form, because of the potential for over-broad
exception handling:
>>> try:
... y = x["10"]
... except:
... y = None
In general, try to catch the exception most specific to your code:
>>> try:
... y = x[10]
... except IndexError:
... y = None
...because then you will see the errors you didn’t plan for:
>>> try:
... y = x["10"]
... except IndexError:
... y = None
Traceback (most recent call last):
...
TypeError: list indices must be integers
Incidentally, you can re-raise exceptions, potentially after doing
something else:
>>> try:
... y = x[10]
... except IndexError:
... # do something else here #
... raise
Traceback (most recent call last):
...
IndexError: list index out of range
There are some special exceptions to be aware of. Two that I run into a lot
are SystemExit and KeyboardInterrupt. KeyboardInterrupt is what is raised
when a CTRL-C interrupts Python; you can handle it and exit gracefully if
you like, e.g.
>>> try:
... # do_some_long_running_task()
... pass
... except KeyboardInterrupt:
... sys.exit(0)
which is sometimes nice for things like Web servers (more on that tomorrow).
SystemExit is also pretty useful. It’s actually an exception raised by
sys.exit, i.e.
>>> import sys
>>> try:
... sys.exit(0)
... except SystemExit:
... pass
means that sys.exit has no effect! You can also raise SystemExit instead
of calling sys.exit, e.g.
>>> raise SystemExit(0)
Traceback (most recent call last):
...
SystemExit: 0
is equivalent to sys.exit(0):
>>> sys.exit(0)
Traceback (most recent call last):
...
SystemExit: 0
Another nice feature of exceptions is exception hierarchies.
Exceptions are just classes that derive from Exception, and you
can catch exceptions based on their base classes. So, for example,
you can catch most standard errors by catching the StandardError
exception, from which e.g. IndexError inherits:
>>> print issubclass(IndexError, StandardError)
True
>>> try:
... y = x[10]
... except StandardError:
... y = None
You can also catch some exceptions more specifically than others. For
example, KeyboardInterrupt inherits from Exception, and some times you
want to catch KeyboardInterrupts while ignoring all other exceptions:
>>> try:
... # ...
... pass
... except KeyboardInterrupt:
... raise
... except Exception:
... pass
Note that if you want to print out the error, you can do coerce a string
out of the exception to present to the user:
>>> try:
... y = x[10]
... except Exception, e:
... print 'CAUGHT EXCEPTION!', str(e)
CAUGHT EXCEPTION! list index out of range
Last but not least, you can define your own exceptions and exception
hierarchies:
>>> class MyFavoriteException(Exception):
... pass
>>> raise MyFavoriteException
Traceback (most recent call last):
...
MyFavoriteException
I haven’t used this much myself, but it is invaluable when you are writing
packages that have a lot of different detailed exceptions that you might
want to let users handle.
(By default, I usually raise a simple Exception in my own code.)
Oh, one more note: AssertionError. Remember assert?
>>> assert 0
Traceback (most recent call last):
...
AssertionError
Yep, it raises an AssertionError that you can catch, if you REALLY want to...