Exception as the primary error handling mechanism?

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Fri Jan 1 09:49:02 EST 2010


On Fri, 01 Jan 2010 00:26:09 -0500, Benjamin Kaplan wrote:

> On Thu, Dec 31, 2009 at 11:47 PM, Peng Yu <pengyu.ut at gmail.com> wrote:
>> I observe that python library primarily use exception for error
>> handling rather than use error code.
>>
>> In the article API Design Matters by Michi Henning
>>
>> Communications of the ACM
>> Vol. 52 No. 5, Pages 46-56
>> 10.1145/1506409.1506424
>> http://cacm.acm.org/magazines/2009/5/24646-api-design-matters/fulltext
>>
>> It says "Another popular design flaw—namely, throwing exceptions for
>> expected outcomes—also causes inefficiencies because catching and
>> handling exceptions is almost always slower than testing a return
>> value."
>>
>> My observation is contradicted to the above statement by Henning. If my
>> observation is wrong, please just ignore my question below.
>>
>> Otherwise, could some python expert explain to me why exception is
>> widely used for error handling in python? Is it because the efficiency
>> is not the primary goal of python?
>> --
>> http://mail.python.org/mailman/listinfo/python-list
>>
>>
> Read the quote again "Another popular design flaw—namely, throwing
> exceptions *for expected outcomes*"
> In Python, throwing exceptions for expected outcomes is considered very
> bad form (well, except for StopIteration but that should almost never be
> handled directly by the programmer).


Exceptions are *exceptional*, not "errors" or "unexpected". They are 
exceptional because they aren't the "normal" case, but that doesn't mean 
they are surprising or unexpected. Are you surprised that your "for x in 
range(1000)" loop comes to an end? Of course you are not -- it is 
completely expected, even though less than 1% of the iterations are the 
last loop. The end of the sequence is EXCEPTIONAL but not UNEXPECTED.

If you program without expecting that keys can sometimes be missing from 
dictionaries (KeyError), or that you might sometimes have to deal with a 
list index that is out of range (IndexError), or that the denominator in 
a division might be zero (ZeroDivisionError), then you must be writing 
really buggy code. None of these things are unexpected, but they are all 
exceptional.

The urllib2 module defines a HTTPError class, which does double-duty as 
both an exception and a valid HTTP response. If you're doing any HTTP 
programming, you better expect to deal with HTTP 301, 302 etc. codes, or 
at least trust that the library you use will transparently handle them 
for you.


> To answer why people recommend using "Easier to Ask Forgiveness than
> Permission" as opposed to "Look Before You Leap" : Because of the way
> it's implemented, Python works quite differently from most languages. An
> attribute look-up is rather expensive because it's a hash table look-up
> at run time. Wrapping a small piece of code in a try block however,
> isn't (relatively) very expensive at all in Python.

It's not just relatively inexpensive, it's absolutely inexpensive: it 
costs about as much as a pass statement in CPython, which is pretty much 
as cheap as it gets. (If anyone can demonstrate a cheaper operation 
available from pure Python, I'd love to see it.)


> It's only catching the exception that's expensive, 

True.


> but if you're catching the exception, 
> something has gone wrong anyway and performance isn't your biggest
> issue.


The second try...except clause in the urllib2 module reads:

try:
    kind = int(kind)
except ValueError:
    pass

In this case, the error is simply ignored. Nothing has gone wrong.


Here's an example from my own code: I have an API where you pass a 
mapping (either a dict or a list of (key, value) tuples) to a function. 
So I do this:

try:
    it = obj.iteritems()
except AttributeError:
    it = obj
for key, value in it:
    do_stuff()



There's nothing wrong with catching exceptions.


-- 
Steven



More information about the Python-list mailing list