Exception as the primary error handling mechanism?

MRAB python at mrabarnett.plus.com
Sun Jan 3 18:28:27 EST 2010


Michi wrote:
> On Jan 1, 2:47 pm, Peng Yu <pengyu... at gmail.com> wrote:
>> In the article API Design Matters by Michi Henning
>>
>> Communications of the ACM
>> Vol. 52 No. 5, Pages 46-56
>> 10.1145/1506409.1506424http://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.
> 
> Seeing that quite a few people have put their own interpretation on
> what I wrote, I figured I'll post a clarification.
> 
> The quoted sentence appears in a section of the article that deals
> with efficiency. I point out in that section that bad APIs often have
> a price not just in terms of usability and defect rate, but that they
> are often inefficient as well. (For example, wrapper APIs often
> require additional memory allocations and/or data copies.) Incorrect
> use of exceptions also incurs an efficiency penalty.
> 
> In many language implementations, exception handling is expensive;
> significantly more expensive than testing a return value. Consider the
> following:
> 
> int x;
> try {
>     x = func();
> } catch (SomeException) {
>    doSomething();
>    return;
> }
> doSomethingElse();
> 
> Here is the alternative without exceptions. (func() returns
> SpecialValue instead of throwing.)
> 
> int x;
> x = func();
> if (x == SpecialValue) {
>     doSomething();
>     return;
> }
> doSomethingElse();
> 
> In many language implementations, the second version is considerably
> faster, especially when the exception may be thrown from deep in the
> bowels of func(), possibly many frames down the call tree.
> 
> If func() throws an exception for something that routinely occurs in
> the normal use of the API, the extra cost can be noticeable. Note that
> I am not advocating not to use exceptions. I *am* advocating to not
> throw exceptions for conditions that are not exceptional.
> 
> The classic example of this are lookup functions that, for example,
> retrieve the value of an environment variable, do a table lookup, or
> similar. Many such APIs throw an exception when the lookup fails
> because the key isn't the table. However, very often, looking for
> something that isn't there is a common case, such as when looking for
> a value and, if the value isn't present already, adding it. Here is an
> example of this:
> 
> KeyType k = ...;
> ValueType v;
> 
> try {
>    v = collection.lookup(k);
> } catch (NotFoundException) {
>    collection.add(k, defaultValue);
>    v = defaultValue;
> }
> doSomethingWithValue(v);
> 
> The same code if collection doesn't throw when I look up something
> that isn't there:
> 
> KeyType k = ...;
> ValueType v;
> 
> v = collection.lookup(k);
> if (v == null) {
>     collection.add(k, defaultValue);
>     v = defaultValue;
> }
> doSomethingWithValue(v);
> 
> The problem is that, if I do something like this in a loop, and the
> loop is performance-critical, the exception version can cause a
> significant penalty.
> 
In Python, of course, there's a method for this: setdefault.

> As the API designer, when I make the choice between returning a
> special value to indicate some condition, or throwing an exception, I
> should consider the following questions:
> 
>  * Is the special condition such that, under most conceivable
> circumstances, the caller will treat the condition as an unexpected
> error?
> 
>  * Is it appropriate to force the caller to deal with the condition in
> a catch-handler?
> 
>  * If the caller fails to explicitly deal with the condition, is it
> appropriate to terminate the program?
> 
> Only if the answer to these questions is "yes" is it appropriate to
> throw an exception. Note the third question, which is often forgotten.
> By throwing an exception, I not only force the caller to handle the
> exception with a catch-handler (as opposed to leaving the choice to
> the caller), I also force the caller to *always* handle the exception:
> if the caller wants to ignore the condition, he/she still has to write
> a catch-handler and failure to do so terminates the program.
> 
> Apart from the potential performance penalty, throwing exceptions for
> expected outcomes is bad also because it forces a try-catch block on
> the caller. One example of this is the .NET socket API: if I do non-
> blocking I/O on a socket, I get an exception if no data is ready for
> reading (which is the common and expected case), and I get a zero
> return value if the connection was lost (which is the uncommon and
> unexpected case).
> 
> In other words, the .NET API gets this completely the wrong way round.
> Code that needs to do non-blocking reads from a socket turns into a
> proper mess as a result because the outcome of a read() call is tri-
> state:
> 
>  * Data was available and returned: no exception
> 
>  * No data available: exception
> 
>  * Connection lost: no exception
> 
> Because such code normally lives in a loop that decrements a byte
> count until the expected number of bytes have been read, the control
> flow because really awkward because the successful case must be dealt
> with in both the try block and the catch handler, and the error
> condition must be dealt with in the try block as well.
> 
> If the API did what it should, namely, throw an exception when the
> connection is lost, and not throw when I do a read (whether data was
> ready or not), the code would be far simpler and far more
> maintainable.
> 
> At no point did I ever advocate not to use exception handling.
> Exceptions are the correct mechanism to handle errors. However, what
> is considered an error is very much in the eye of the beholder. As the
> API creator, if I indicate errors with exceptions, I make a policy
> decision about what is an error and what is not. It behooves me to be
> conservative in that policy: I should throw exceptions only for
> conditions that are unlikely to arise during routine and normal use of
> the API.
> 
In another area, string slicing in C# uses the Substring method, where
you provide the start position and number of characters. If the start
index is out of bounds (it must be >= 0 and < length) or the string is
too short, then it throws an exception. In practice I find Python's
behaviour easier to use (and the code is shorter too!).

C# also misses Python's trick (in Python 2.6 and above) of giving string
instances a format method, instead making it a class method, so you need
to write:

     string.format(format_string, ...)

instead of Python's:

     format_string.format(...)

On the other hand, C#'s equivalent of raw strings treat backslashes
always as a normal character. I think it's the only feature of C#'s
string handling that I prefer to Python's.



More information about the Python-list mailing list