"Oivvio Polite" <oiv...@cajal.mbb.ki.se> wrote in message news:email@example.com... > Hi all. > > I'm building a class with a uniform interface and I'm trying to come to > terms with polymorphism in python.
Hmmm... I think you want "overloading" (methods that do different things depending on their arguments' types) rather than "polymorphism" (calling method upon an object, without caring which actual type that object IS, as long as it provides methods that Do The Right Thing). A subtle distinction, to be sure, but an important one.
> For instance I want the constructor to take either a filename, a file object > or a database connection object. > In C++ this would be done using three different constructors. (I'm not quite > sure about this, cause I don't know C++ :-)
Yes: you would use three different overloads of the constructor.
> But how should one do it in python.
My instinctive approach (and it may be that I still haven't developed the "right" Python instincts, because I haven't been using it long!) would be :
class myclass: def __init__(filename=None, fileobject=None, dbconn=None): if filename: # initialize using the filename elif fileobject: # initialize using the fileobject elif dbconn: # initialize using the db connection else: # throw an exception, or whatever
Client-code would then use, e.g.,
myobj = myclass("foo.dat") or myobj = myclass(filename="foo.dat")
to initialize with a filename,
myobj = myclass(fileobject=open("foo.dat"))
to initialize with a file object, etc.
The alternative, given a single, "anodyne" argument to __init__, would be for myclass to try to find out if it "BEHAVES like a filename string", "BEHAVES like a fileobject", etc, rather than trying to ascertain whether it "IS" one of those. "The IS of identity", bemoaned by Korzibsky, is just as generally inappropriate in Python as he claimed it to be in terms of fuzziness of thinking; and Wittgenstein before him pointed out how language-games, and NOT mathematical-identity, determine applicability of a term-in-context.
In other words, don't check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly what subset of duck-like behaviour you need to play your language-games with. If the argument fails this specific-ducklyhood-subset-test, then you can shrug, ask "why a duck?" (at least, you can if you're a Marx Brothers fan and have memorized "Cocoanuts"' script; Monty Python one-true-wayists will have to find their own simile here), and move on to the next set of tests (why-a-no- chicken immediately comes to mind, but then one would have to ask why it crosses the road, so I think we'd better snip it).
On the other hand, this can be a considerable amount of work, depending on how you go about it (actually, it need not be that bad if you "just go ahead and try", of course catching the likely exceptions if the try does not succeed; but still, greater than 0).
Besides, "explicit is better than implicit", goes one of Python's mantras. Just let the client-code explicitly TELL you which kind of argument they are passing you (and doing so through a named argument is simple and readable), and your work drops to zero, while removing no useful functionality whatever from the client. As a little vig, you also avoid trouble in case what the client wants to pass is some tricky object that behaves EITHER as a file connection OR as a db connection (etc, etc) -- not all that likely, but, who knows.
About typeswitching in general...:
> Looking at resent posts I found a thread on type checking that contained > this unsettling post: > > <pa...@prescod.net> wrote: > > Let me also point out that in many cases, this style of programming > > would be frowned upon by serious Python programmers. For instance, if > > you check that something is a string, your code will complain when it is > > handed a Unicode string, even though it would probably work fine. If you > > check that it is an open file, then your code probably will complain > > about stringIO file-like objects, even though it would probably work > > fine. If you check that it is an integer, your code will complain about > > integers, even though ti would probably work fine.
Amen, hallelujah. You don't really care for IS-A -- you really only care for BEHAVES-LIKE-A-(in-this-specific-context), so, if you do test, this behaviour is what you should be testing for (although I assume Paul Prescod meant "longs" where he wrote "integers" on the last line, or else I don't know what those two last lines mean).
> What are your experiences? Does the type(arg) checking really become > unmanageable as Paul's post implies? If so what are the alternative routes?
I have no direct experience of setting up such typeswitching *in Python*, but I *do* know it becomes unmanageable, for various reasons, in languages where I painted myself into that corner. Stroustrup had the same experience with typeswitching in Simula-67, which is why he so carefully kept it out of C++ (it snuck back in as dynamic_cast, but that is _supposed_ to be used as the closest C++ equivalent of does-it-support-this-functionality-set [by dyncasting to a suitable pointer-to-abstract-interface] -- although of course it is just as easy to mis-use it as a typeswitch, sigh). However, the languages I've fallen into this particular pitfall in were not as dynamic/fluid as Python is, so I can't be "sure because of personal experience" that it is indeed a danger; still, all of my instincts ARE yelling "yes, yes, exactly!" in chorus with Paul Prescod (and some of my favourite thinkers, as above quoted, appear to provide solid back-up general reasons:-).
The "royal-road" alternative route to overloading would, I think, be the use of suitable named-arguments. A rockier road, perhaps preferable in some cases, but more work for dubious benefit, would be the try/except approach to see if an argument supplies the functionalities you require.