Is this an OK design?

urnerk at qwest.net urnerk at qwest.net
Wed Apr 14 22:43:17 EDT 2004


Partly because I'm doing OpenOffice PowerPoint (not much room on a slide),
I'm breaking what could be a single class into a succession of tiny
"version" classes, each inheriting from the one before, and each adding
more functionality.  

I'm also separating each version into a module of its own, such that each
successive version keeps the same name (say P), and inherits from module.P

Part of the goal is to demonstrate inheritance, so it's OK to evolve my P
class over several versions.

Here's a skeletal outline, where --- separates modules:

---- pv1.py ----

  class P(object):
     def __init__(self, amap)
         self.amap = amap

--- pv2.py ---

  import pv1
  class P(pv1.P):

    def __invert__(self):
       # newmap = inverted self.amap
       return P(newmap)

    def __mul__(self,other): etc.

--- pv3.py ---

  import pv2
  class P(pv2.P):
    def __div__(self,other):
       return self * ~other

---

Note that only the base class has a constructor, while the subclasses add
additional methods.

The above design had a problem though:

  >> import pv1, pv2, pv3
  >> from pv3 import P
  >> p1 = P(map)
  >> q = ~p1  # invokes __invert__ in pv2
  >> q/p1
  Error about / not defined for instance, instance
  >> isinstance(q,pv3.P)
  False
  >> isinstance(q,pv2.P)
  True

The problem was __invert__, being defined in pv2, would return an instance
of pv2.P, which doesn't yet know about __div__ (that comes in the next
version).

So the need is to have methods that return a P, to return a P of the same
version, e.g. if p1 is of type pv3.P, then __invert__, even though defined
in the parent, should return a pv3.P when invoked.

My solution was:

---- pv1.py ----

  class P(object):
     def __init__(self, amap)
         self.amap = amap
         self.P = self.__class__ # <-- store type of instance

--- pv2.py ---

  import pv1
  class P(pv1.P):

    def __invert__(self):
       # newmap = inverted self.amap
       return self.P(newmap)  # <--- class is now object attribute

    def __mul__(self,other): etc.

--- pv3.py ---

  import pv2
  class P(pv2.P):
    def __div__(self,other):
       return self * ~other

---

Now when I go:

  >> from pv3 import P
  >> p1 = P(map)
  >> q = ~p1  # invokes __invert__
  >> q/p1
  <answer>
  >> isinstance(q,pv3.P)
  True
  >> isinstance(q,pv2.P)
  True

i.e. q/p1 succeeds, because ~p1 returns a pv3.P type, which understands
about __div__.

What I'd like to know is:  is my solution perverse or Pythonic?  Is there a
better way?

Kirby




More information about the Python-list mailing list