[Tutor] How to handle exceptions raised inside a function?

Steven D'Aprano steve at pearwood.info
Tue Nov 30 22:23:43 CET 2010


Richard D. Moores wrote:
> Please take a look at 2 functions I just wrote to calculate the
> harmonic and geometric means of lists of positive numbers:
> <http://tutoree7.pastebin.com/VhUnZcma>.
> 
> Both Hlist and Glist must contain only positive numbers, so I really
> need to test for this inside each function. But is there a good way to
> do this? What should the functions return should a non-positive number
> be detected? Is there a conventional Pythonic way to do this?

There are two basic approaches to handling errors in Python:

(1) Don't do any error checking at all. If the input is bad, an 
exception will (hopefully!) be raised. Provided you know that bad input 
*will* lead to an exception, and not just plausible-looking but 
incorrect result, this is often the simplest way.

(2) If you don't trust that a sensible exception will be raised, then do 
your own error checking, and raise an exception.

For numeric work, another approach is to return a floating point NAN 
("Not A Number"). Unfortunately Python doesn't give any standard way to 
specify *which* NAN is returned, but you can return float("nan") to 
return one of them.

A fourth approach, rare in Python, is to return some sort of magic value 
to indicate an exceptional case. Just about the only example of this I 
can think of is string.find(), which returns -1 to indicate "not found".

A fifth approach, common in some other languages, is to return some 
arbitrary value, and set an error flag. The caller then has to write 
code like this:

result = function(arguments)
if not last_result_error:
     # no error occurred
     print "result is", result


If you do this, I will *personally* track you down and beat you to death 
with a rather large fish.

*wink*


For what it's worth, I have a module of statistics functions (shameless 
plug: http://pypi.python.org/pypi/stats and 
http://code.google.com/p/pycalcstats -- feedback and bug reports 
welcome) that includes the harmonic and geometric mean. My harmonic mean 
looks like this:

def harmonic_mean(data):
     try:
         m = mean(1.0/x for x in data)
     except ZeroDivisionError:
         return 0.0
     if m == 0.0:
         return math.copysign(float('inf'), m)
     return 1/m

Notice that if the data includes one or more zeroes, the harmonic mean 
itself will be zero: limit as x->0 of 1/x -> infinity, and 1/infinity -> 
0. If the sum of reciprocals itself cancels to zero, I return the 
infinity with the appropriate sign. The only exceptions that could occur 
are:

* mean will raise ValueError if the data is empty;
* if an argument is non-numeric, TypeError will occur when I take the 
reciprocal of it.



-- 
Steven


More information about the Tutor mailing list