On Nov 12, 2008, at 10:45 AM, Tim Rowe wrote:

What do you actually mean by "Quacks like a string"? Supports the
'count()' method? Then you find out if it doesn't when you try to
apply the 'count()' method. Supports some method that you don't
actually use? Then why do you care?

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 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.

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."

I have to say "pretty much" since obviously there may be some evil context that actually checks isinstance. But that's the pathological case, and we shouldn't let it prevent us from neatly handling the typical case.

The point about duck typing is that something might quack like a duck
but not walk like a duck -- one of those duck calls that hunters use,
for instance. Quacking like a duck doesn't actually mean it /is/ a
duck, it means that it will do instead of a duck if the quack is all
you want.

Well, that's one point, but it's not the only point. If I have code that expects to be working with strings, and I want to purposely give it something else, then it's reasonable to expect that the something- else will act like a string in every way that a string is likely to be exercised. My string wrapper or doppleganger_string or whatever should implement all the methods, and support all the operators and type conversions, that basestring does.

If you need to know that it walks like a duck, mates like a duck and
tastes like a duck when roasted, you probably want it to really /be/ a
duck and should go back to inheritance.

I can't agree; there are times when inheritance just won't do, for example when you don't have control over the object creation, because they come from some factory method you can't change. In that case you may need to make a wrapper instead of a subclass, but if you've faithfully implemented the interface of the original class, you should be able to use it wherever the original class could be used (within reason).

So, since it's pretty clear by now that there's no standard idiom for this, I'll try to cook up something myself. For classes, I think we could do a two-stage test:

1. If the given object isinstance of the specified class, then all is good and return immediately. 2. Otherwise, check each of the public attributes of the specified class, and make sure that the given object has corresponding callable attributes.

For case 2, we might be able to cache the result so that we don't do all that work again the next time the same type comparison is done.

Anyway, I'll evolve something in our shop here and live with it a while, and in a few months I'll either share what we develop for this purpose, or admit it was a horrible idea all along. :)

Cheers,
- Joe

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to