Exceptions - The Dark Side of the Force

Jan 13, 2016

A recent blog post “If you don’t like exceptions, you don’t like
Python”
has made rounds lately, and compelled me to write a partial rebuttal. It
is not like that blog post is completely wrong, but it is not the be-all
and end-all of this topic. And if I may add, it is kind of opinionated.

The original article states that exceptions are central to Python, that
the common advice “exceptions should only be for errors, not for normal
flow control” is wrong, goes on, explaining that exceptions are used in
core implementations, such as the iterator protocol, and attribute
access, thus that they are a central feature of the language. Some
longer parts of the blog posts are concerned debunking commonly held
misconceptions by Java and C++ programmers.

Roughly speaking, exceptions in this article are portrait very
favourably, with all that praise, all criticism and questions regarding
their use are eclipsed.

Use exceptions for error handling

This is a point where I just whole-heartedly agree with barnert. Errors
should be propagated using exceptions, so

defmin(*lst):ifnotlst:raiseValueError("min(..) requires a list with at least one element")minimum=lst[0]foriteminlst:ifminimum>item:minimum=itemreturnminimum

is a perfectly fine usage of exceptions, and callers should check for
these exceptions if their code does not guarantee that the argument is a
list of length above 0.

This snippet has many exception-related issues and shows how not to use
exceptions. First of all, it is unclear which key-access in the try
block does raise the exception. It could be in bar[key], or in
_[anotherkey], then in res[evenanotherkey], or finally it could
happen in dosomething(foo). The exception mechanism dissociates error
handling from the values and variables. My question is: can you tell
whether catching KeyErrors from dosomething() is intended?

So when using exceptions, one has to be really careful about which
exceptions are caught and which aren’t. With defensive programming (i.e.
haskey())-style checks, it is unambiguous and hardly as “intrusive” to
the code as writing out individual try-catch blocks for each indexing
operation.

Exceptional Dangers

So there are basically two risks when using exceptions:

An exception that should be caught is not caught

An exception is caught wrongfully

The first risk is definitely a risk, but one that I don’t worry too much
about. The second is a risk I definitely fear. How many functions in
your code can throw KeyErrors, ValueError, IndexError,
TypeError, and RuntimeError can your code throw?

Exceptions as Pythonic gotos

Exceptions can emulate goto statements. Of course they are jumps to
upper levels on the stack, but also within statements. In C code, goto’s
are a primary means of function-local control flow and error handling
(and for error-handling, they are rather uncontroversial):

intmax_in_two_dim(double*array,size_tN,size_tM,double*out){if(N*M==0)gotoempty_array_lbl;doublemax=array[0];for(inti=0;i<N;++i){for(intj=0;j<M;++j){doubleval=array[j*N+k];if(val!=val)// NaN case
gotoerr_lbl;if(max<val)max=val;}}return0;nan_lbl:fprintf(stderr,"encountered a not-a-number value when unexpected");return-1;empty_array_lbl:fprintf(stderr,"no data in array with given dims");return-2;}

You can model this usage with exceptions in Python. I have seen such
code in the wild.

In most cases there are ways to avoid this pattern that are preferrable.
Python’s for loops have an optional else branch that helps avoiding
such jumps. Nevertheless, this pattern can go awry with a RuntimeError
happending at some other place in the loop, etc.

Meta: Ingroup, Outgroup Thinking

What I disklike the most about barnert’s article is probably mostly what
one can read in the title: “If …, you don’t like Python”. It is in
line with a lot of talk I hear about code/software/solutions being
“Pythonic”. What this seems to imply is, that must take sides: Either
you are in line with an orthodox Python community, or you are an
outsider, someone who is not “Pythonic” enough. All of this is not
helpful for improving code.

Conclusion

Exceptions are a central and powerful tool in Python. But use them with
care and caution. Do not pretend that they are like a magic wand, don’t
use them to show your love for python. Use them when the individual
situation calls for exception usage.