Google Groups

polymorphism (was Re: Type checking in python?)


Alex Martelli Jul 26, 2000 12:00 AM
Posted in group: comp.lang.python
"Oivvio Polite" <oiv...@cajal.mbb.ki.se> wrote in message
news:8lmo4i$s28$1@news.kth.se...
> 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.


Alex