Interface and duck typing woes

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Aug 28 19:24:58 EDT 2013


On Wed, 28 Aug 2013 18:09:22 -0300, Joe Junior wrote:

> While designing a simple library, I found myself asking a philosophical
> question: to check or not to check the parameter's interface?

The only correct answer to that is, "Yes no maybe".

:-)


> I think that, considering it is Python, the usual answer would be "no",
> but here is the situation that got me thinking:
> 
> class Flock:
> 
>     def __init__(self):
>         self.ducks= []
> 
>     def do_stuff(self):
>         for duck in self.ducks:
>             duck.quack()
> 
> class Duck:
> 
>     def quack(self):
>         #quack-quack
>         pass
> 
> f = Flock()
> d = Duck()
> f.ducks.append(d)
> f.do_stuff()
> 
> Ok, no big deal there, the problem is if the user forgets to implement
> the quack() method. The stack trace would complain that "duck.quack()"
> is wrong, but that can happen hundreds of lines after the user actually
> added the object to the Flock, and it can be hard to find out what is
> happening and which object is wrong.

True, but that's a good case for improving the error message, or using a 
debugger. Here is Flock.do_stuff re-written to give a more verbose/useful 
error message:


    def do_stuff(self):
        for i, duck in enumerate(self.ducks):
             try:
                 duck.quack()
             except AttributeError:
                 raise DuckError(
                         'object %r at index %d has no quack' % (duck, i)
                         )


Okay, seeing the index is useful. But we would have got nearly as much 
information from the AttributeError traceback, minus the index:

py> (42).quack()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'quack'

So how much extra work are you prepared to put in to make a rare 
occurrence (passing a magpie instead of a duck) easier to debug? Since 
errors are presumably rare, maybe the answer is "not a lot of extra 
work". But if the consequence of an error is catastrophic ("for want of a 
duck quacking, the nuclear reactor explodes, making the northern 
hemisphere uninhabitable"), maybe the answer is "as much as it takes".

Other questions: what happens if duck.quack is buggy and raises 
AttributeError? A good question, but just how far should we go in 
worrying about things like this? What happens if duck.quack is buggy and 
raises StopIteration? Sometimes the right reaction is to deal with it if 
and when it actually occurs. In other words, wait for the bug report 
before trying to fix it.

("Fix it" may mean telling people "don't do that!".)


> Of course I don't want to check isistance(), I like duck typing, but
> should I check if hasattr() and callable() before adding to the
> container? What is the pythonic way to deal with it? Am I worrying too
> much ;-)?

Yes :-)

Except in the (rare?) case that you aren't worrying enough, in which case 
you can check hasattr and callable up front, or do whatever other tests 
you feel the need to check. It depends on the specific code you are 
writing.



-- 
Steven



More information about the Python-list mailing list