overloading on type?

Jay O'COnnor oconnor at bioreason.com
Wed Feb 14 16:53:58 CET 2001


Burkhard Kloss wrote:

> One design problem I run across frequently in Python is how to nicely deal
> with different types of arguments.  For example, writing a date class you'd
> often have overloaded constructors in a statically typed language like C++:
>
> class date
> {
> public:
>     date (const date &);
>     date (const string &);
>     date (long);
>
> in python, I catch myself writing things along the lines of
>
> class date:
>     def __init__ (self, arg):
>         if type(arg) == types.InstanceType:
>             ....
>         elif type(arg) == types.IntType:
>             ...
>
> which works, but just seems so *wrong*.
>
> Maybe I'm just having a conceptual block here as a result of my upbringing
> (I'm a longtime and unrepentant C++ user and lover), but surely in a
> language as nice as python there must be a better way to do this? :)
> Overloading obviously doesn't fit into the conceptual framework of the
> language.

One approach you can use is called "Double Dispatch".  This will allow you to
resolve to a specific method from a general method based on the type of the
argument; even in dynamically bound languages susch as Python and Smalltalk.

The approach is to get the argument invovled in the decision as well. Your
receiver object calls back to the argument object telling it to do something
with itself.  The argument object, in turn, calls back to the first object with
a specific message based on it's own type (which it already knows)

Here's an example using a simple DrawingContext and Shape hierarchy

                         #!/usr/bin/python
                         #covarient.py

                         class DrawingContext:
                                 def drawShape (self, aShape):
                                         aShape.drawOn(self)

                                 def drawCircle (self, aCircle):
                                         print "Drawing circle at " +
aCircle.printLocation()

                                 def drawSquare (self, aSquare):
                                         print "Drawing square at " +
aSquare.printLocation()

                         class Shape:
                                 def __init__(self, x=0, y=0):
                                         self.x = x
                                         self.y = y

                                 def printLocation (self):
                                         return "X = " + `self.x` + " Y= " +
`self.y`

                                 def drawOn(self, aDrawingContext):
                                         #Abstract method
                                         pass

                         class Circle(Shape):

                                 def __init__ (self, x=0, y=0, radius=0):
                                         Shape.__init__(self, x,y)
                                         self.radius = radius

                                 def printLocation (self):
                                         return Shape.printLocation(self) + "
Radius = " + `self.radius`

                                 def drawOn (self, aDrawingContext):
                                         aDrawingContext.drawCircle(self)

                         class Square(Shape):

                                 def __init__ (self, x=0, y=0, sideLength=0):
                                         Shape.__init__(self, x,y)
                                         self.sideLength = sideLength

                                 def printLocation (self):
                                         return Shape.printLocation(self) + "
Side Length = " + `self.sideLength`

                                 def drawOn (self, aDrawingContext):
                                         aDrawingContext.drawSquare(self)

                         # Test script starts here

                         dc = DrawingContext()
                         circle = Circle (x=100,y=200,radius=5)
                         square = Square (x=10,y=150, sideLength=10)

                         dc.drawShape( circle)
                         dc.drawShape (square)

and here's the output

                          [jay at altaica misc_junk]$ ./covarient.py
                         Drawing circle at X = 100 Y= 200 Radius = 5
                         Drawing square at X = 10 Y= 150 Side Length = 10

(Note: it's called covarient.py because I took it from some sample code I
posted in another message forum about covarients in Java)

The way it works is that the DrawingContext object must draw the shape, but it
draws it in a different manner based on what kind of shape it is.   So it tells
the given shape to draw on itself (the canvas)  The shape then calls back to
the DrawingContext with the message for the specific drawing operation
(drawCircle(), etc)

This works very well at resolving from an abstract interface to a specific
operation based on the tyhpes of both objects.

It does, however, require that you be able to add code to both partners of the
operation.  If this is not possible, I usually use an approach of building a
Dictionary   where the keys are the type and the values are the methods to
call.  I thjink this looks a little cleaner than having  an 'if' conditional.

        import types

        class Date:
                def __init__(self, fromObject)
                    initializer =initOperations[type(fromObject)]
                    initializer (self, fromObject)

                def initializeFromInt(self, anInt):
                    pass
                def initializeFromInstance (self, anInstance):
                    pass
        initOperations = {
            types.IntType:Date.initializeFromInt,
            types.InstanceType: Date.initializeFromInstance}

Note: I haven't tested that, but you get the idea.

I consider this second approach a not very good one and only appropriate if I
need to make such a decisision either using primitive types or third party
objects that I can't add the douple dispatch support to


Take care,

Jay O'Connor
joconor at cybermesa.com

Python Language Discussion Forum -
http://pub1.ezboard.com/fobjectorienteddevelopmentpython




More information about the Python-list mailing list