EAFP gone wrong

Arnaud Delobelle arnodel at googlemail.com
Tue Feb 9 23:52:35 CET 2010


Hi all,

Hi have a set of classes that represent mathematical objects which can
be represented as a string using a 'latex' method (after Knuth's famous
typesetting system).  As I want to be able to typeset some builtin types as
well, I have a generic function, latex(), as follows:

def latex(val):
    try:
        return val.latex()
    except AttributeError:
        if isinstance(val, (tuple, list)):
            return ", ".join(map(latex, val))
        elif isinstance(val, dict):
            return ", ".join(
                "%s=%s" % (latex(k), latex(v)) 
                for k, v in sorted(val.iteritems())
            )
        else:
            return str(val)

It's EAFP and I have used this for a while with no problem.  Recently I
added a new class for 'n choose r' objects, as follows:

class Choose(Expression):
   def __init__(self, n, r):
        self.subexprs = n, r
        self.n = n
        self.r = r
    def calc(self, ns=None, calc=calc):
        return choose(calc(self.n, ns), calc(self.r, ns))
    def latex(self):
        return "{%s \\choose %s}" % (latex(self.n), latex(self.k))

When I create a Choose object and try to get its latex representation,
this happens:

>>> c = Choose(5, 3)
>>> latex(c)
'<qmm.maths.expressions.Choose object at 0x17c92d0>'

This puzzled me for a bit: why is it not trying to use the latex()
method of the Choose object?  I read and reread the definition of the
latex() method for a while until I found that there was a typo.  Where
it says:

    latex(self.k)

it should say:

    latex(self.r)

Thus it triggers an AttributeError, which is exactly the kind of
exception that I am catching in the latex() function after trying
val.latex(). (Of course I could have caught this by calling c.latex()
directly but it's such a short method definition that I couldn't imagine
missing the typo!).

This means that EAFP made me hide a typo which would have been obviously
detected had I LBYLed, i.e. instead of

    try:
        return val.latex()
    except AttributeError:
        ...

do

    if hasattr(val, 'latex'):
        return val.latex()
    else:
        ...


So was it wrong to say it's EAFP in this case?  Should I have known to
LBYL from the start?  How do you decide which one to use?  Up to now, I
thought it was more or less a matter of taste but now this makes me
think that at least LBYL is better than catching AttributeError.

Thanks for any guidance.

-- 
Arnaud



More information about the Python-list mailing list