duck-type-checking?

Ben Finney bignose+hates-spam at benfinney.id.au
Thu Nov 13 03:26:37 EST 2008


Joe Strout <joe at strout.net> writes:

> Because if I write a method with the intention of treating the
> arguments like strings in various ways (slicing, combining with
> other strings, printing to stdout or writing to a file, etc. etc.),
> and some idiot (i.e. me six weeks later or long after I should have
> gone to bed) manages to accidentally pass in something else, then I
> want my program to blow up right away, not plant a roadside bomb and
> cheerfully wait for me to drive by.

This is better achived, not by littering the functional code unit with
numerous assertions that obscure the normal function of the code, but
rather by employing comprehensive unit tests *separate from* the code
unit.

This suite of unit tests is then employed and executed any time the
code changes, and indivates any regressions in functionality from what
is asserted in the tests. Meanwhile, the code unit itself is clear and
free from the masses of error checking that one often finds in code
written without such unit test suites.

> This is not hypothetical -- just last week I had a
> hard-to-track-down abend that ultimately turned out to be an
> NLTK.Tree object stored someplace that I expected to only contain
> strings. I found it by littering my code with assertions of the form
> isinstance(foo,basestring). If I'd had those in there in the first
> place, not only documenting my assumptions but letting the computer
> check them for me, it would have saved me a lot of grief.

Rather than littering these assertions *within* the code unit, and
thereby making that code harder to read later, those assertions would
have been better placed in a unit test module that tests the behaviour
of the unit in response to environment and input.

This has the additional benefit of highlighting parts of one's code
that do not have well-defined interfaces: if the code's behaviour in
response to a simple, narrow specification of inputs and environment
is not easy to describe in a simple true-or-false testable assertion,
then the interface and/or the internals of the code unit is very
likely too complex to be maintained well.

> But in the spirit of duck-typing, I shouldn't actually check that
> foo is a basestring. I should instead check that foo quacks like a
> basestring. I'd define that is:
> 
> "x quacks like a basestring if it implements all the public methods
> of basestring, and can be used in pretty much any context that a
> basestring can."

That is not duck typing. Rather than checking what foo does in
response to prodding that, by your admission, is only designed to find
out what type it is, duck typing instead advocates that you should use
foo *as though it is known to be* the type of object you want. If it
is not suitable, then appropriate exceptions will be raised and either
caught by some code that knows how to handle them, or crash the
program.

Unit tests then come into play by feeding inputs to your code unit and
asserting that the code behaves appropriately whether fed correct or
incorrect inputs: correct inputs, even of wholly unanticipated types,
should cause correct behaviour (i.e. if the code is designed to handle
ducks it should be content with a goose also); and incorrect inputs
are incorrect by definition *only if* they cause the code unit to be
unable to behave correctly (in which case an appropriate exception
should be raised).

-- 
 \         “Pinky, are you pondering what I'm pondering?” “I think so, |
  `\    Brain, but Zero Mostel times anything will still give you Zero |
_o__)                                  Mostel.” —_Pinky and The Brain_ |
Ben Finney



More information about the Python-list mailing list