Adding an interface to existing classes

Ian Kelly ian.g.kelly at gmail.com
Sat Dec 24 20:16:42 EST 2011


On Thu, Dec 22, 2011 at 1:21 AM, Spencer Pearson
<speeze.pearson at gmail.com> wrote:
> I see a problem with this, though. The intersection of two lines is
> (usually) an object of type Point. Since DrawableLine inherits from
> Line, this means that unless I redefine the "intersect" method in
> DrawableLine, the intersection of two DrawableLines will be a Point
> object, not a DrawablePoint. I don't want to saddle the user with the
> burden of converting every method output into its corresponding
> Drawable subclass, and I don't want to redefine every method to return
> an instance of said subclass. I see no other options if I continue
> down the subclassing path. Am I missing something?

You could solve this with a factory. Instead of having
Line.intersection() create a Point directly, have it call
self.factory.create_point().  In the drawing module, replace the
factory for the subclasses with one that creates drawable classes
instead.


> Option 2. A "draw" function, with a function dictionary.
> This feels weird, but is fairly simple to write, use, and extend. We
> have a module with a "draw_functions" dictionary that maps types onto
> functions, and a "draw" function that just looks up the proper type in
> the dictionary and calls the corresponding function. If you create
> your own object, you can just add a new entry to the dictionary. The
> implementation is simple enough to outline here:

This will also work, but inheritance complicates things a little.  Do
you do a type equality check or an isinstance() check when looking up
the type in the dictionary?  If the former, then somebody who
subclasses geometry.Line to create a GroovyLine subclass must add an
entry for GroovyLine to the dictionary, even if the drawing code is
the same.  The drawing code is not inherited.

But if you do an isinstance() check, then you need to be very careful
about how you check it.  If somebody creates a WavyLine subclass and
registers a different drawing method, then you need to make sure the
isinstance() check for the WavyLine implementation happens before the
regular Line implementation, or the wrong drawing code may get invoked
for WavyLine.  Probably the easiest way to do this correctly is to
follow the MRO that Python has conveniently already built for you.
Something like:

def draw(x, *args, **kwargs ):
    for class_ in type(x).__mro__:
        if class_ in draw_functions:
            draw_functions[class_](*args, **kwargs)
            break
    else:
        raise TypeError("don't know how to draw things of type "
                        "{0}".format(type(x)))

Cheers,
Ian



More information about the Python-list mailing list