joe at strout.net
Fri Nov 14 22:49:00 CET 2008
On Nov 14, 2008, at 2:07 PM, Paul McGuire wrote:
> Or to be even more thorough:
> def sub(x: must have getitem, y: must have strip and strip must be
> callable, and y.strip must return something that has replace and
> replace must be callable)
> So even this simple example gets nasty in a hurry, let alone the OP's
> case where he stuffs y into a list in order to access it much later,
> in a completely different chunk of code, only to find out that y
> doesn't support the complete string interface as he expected.
Very true. That's why I think it's not worth trying to be too pure
about it. Most of the time, if a method wants a Duck, you're going to
just give it a Duck.
However, I would like to also handle the occasional case where I can't
give it a Duck, but I can give it something that is a drop-in
substitute for a Duck (really, truly, I promise, and if it blows up
I'll take responsibility for it).
A real-world example from another language (sorry for that, I've been
away from Python for ten years): in REALbasic, there is a Database
base class, and a subclass for each particular database backend
(Postgres, MySQL, whatever). This works fine most of the time, in
that you can write general code that takes a Database object and Does
Stuff with it.
However, all of those database backends are shipped by the vendor, or
by plugin authors -- you can't create a useful Database subclass
yourself, in RB code, because it has a private constructor. So you
end up making your own database class, but that can't be used with all
the code that expects a real Database object.
Of course, the framework design there is seriously flawed (Database
should have been an interface, or at the very least, had a protected
rather than private constructor). And in Python, there's no way to
prevent subclassing AFAIK, so this particular issue wouldn't come up.
But I still suspect that there may be times when I don't want to
subclass for some reason (maybe I'm using the Decorator or Adapter or
Bridge pattern). Yet I'm willing to guarantee that I've adhered to
the interface of another class, and will behave like it in any way
So, the level of assertion that I want to make in a method that
expects a Duck is just that its parameter is either a Duck, or
something that the caller is claiming is just as good as a Duck. I'm
not trying to prevent any possible error; I'm trying to catch the
stupid errors where I inadvertently pass in something completely
different, not duck-like at all (probably because some other method
gave me a result I didn't realize it could produce).
So things like this should suffice:
# simple element
# sequence of elements
# (also "listof_" variants for asserting mutable sequence of whatever)
# dictionary of elements
assert(dictof_like(foo, str, int))
Hmm, I was already forced to change my approach by the time I got to
checking dictionaries. Perhaps a better formalism would be a "like"
method that takes an argument, and something that indicates the
desired type. This could be a tree if you want to check deeper into a
container. Maybe something like:
assert(fits(foo, dictlike(strlike, seqlike(intlike))))
which asserts that foo is something dictionary-like that maps string-
like things to something like a sequence of integer-like things. Most
cases would not be this complex, of course, but would be closer to
But this is still pretty ugly. Hmm. Maybe I'd better wait for
More information about the Python-list