Puzzling OO design problem

Kay Schluehr kay.schluehr at gmx.net
Sat Apr 9 15:00:24 EDT 2005


Hi George,

it's a nice little puzzle and it is more fun to solve it if one is not
a student anymore :)

Filling the gaps in the lattice is somehow necessary but it is not
necessary to create all the classes.

Ansatz:

We can consider two matrices: one is filled with nodes ( class names )
the other is filled with arrows between the nodes representing the
inheritance relationships.

In symbolic notatation:

        | A  B  C |
Nodes = | D  0  F |
        | G  H  I |

         | 0   l    l  |
Arrows = | u   0   u*l |
         | u  u*l  u*l |


Remarks:
1 ) if a node or an arrow is empty a 0 is inserted into the matrix.

2 ) u is for uppermost, l is for leftmost. A multiplication between
    arrows should read as a logical AND: u*l ~ upper AND left.

With this interpretation the above matrizes contains the same
information than the lattice:

A <- B <- C
^    ^    ^
|    |    |
D <- 0 <- F
^    ^    ^
|    |    |
G <- H <- I

Now we need an algorithm to create all the classes from the matrix
information using multiple inheritance when needed:


# arrows symbolized as primes. Only u and l are used in the impl.
# d and r are somewhat more complicated to implement

l = 2
r = 3
u = 5
d = 7

class ClassGridError(Exception):pass

class ClassGrid(object):
    def __init__(self):
       self.class_names = []  # rows of the class-name matrix
       self.arrows      = []  # rows of the arrow matrix
       self.classes     = {}  # store the resulting classes

    def add_names(self,names):
        self.class_names.append(names)

    def add_arrow(self,arrow):
        self.arrows.append(arrow)

    def __repr__(self):
        if self.classes:
            return self.classes.__repr__()
        return object.__repr__(self)

    def create_classes(self):
        for i,class_row in enumerate(self.class_names):
            for j,cls_name in enumerate(class_row):
                if cls_name == 0:
                    continue
                arrow = self.arrows[i][j]
                if arrow == 0:
                   self.classes[cls_name] = type(cls_name,(),{})
                else:
                   bases = []
                   name  = 0
                   if arrow%u == 0:   # search uppermost
                      k = i-1
                      while k>=0:
                         name = self.class_names[k][j]
                         if name:
                            break
                         k-=1
                      if not name:
                         raise ClassGridError,"Wrong arrow matrix"
                      bases.append(self.classes[name])
                   if arrow%l == 0:  # search leftmost
                      k = j-1
                      while k>=0:
                         name = self.class_names[i][k]
                         if name:
                            break
                         k-=1
                      if not name:
                         raise ClassGridError,"Wrong arrow matrix"
                      bases.append(self.classes[name])
                   self.classes[cls_name] =
type(cls_name,tuple(bases),{})

cg = ClassGrid()

cg.add_names(("A","B","C"))
cg.add_names(("D", 0, "F"))
cg.add_names(("G","H","I"))

cg.add_arrow(( 0,  l,   l ))
cg.add_arrow(( u,  0,  u*l))
cg.add_arrow(( u, u*l, u*l))

cg.create_classes()

Now You can checkout Your solution:

>>> cg.classes["A"].__subclasses__()
[<class '__main__.B'>, <class '__main__.D'>]

>>> cg.classes["B"].__subclasses__()
[<class '__main__.C'>, <class '__main__.H'>]

>>> cg.classes["C"].__subclasses__()
[<class '__main__.F'>]

>>> cg.classes["D"].__subclasses__()
[<class '__main__.F'>, <class '__main__.G'>]

>>> cg.classes["F"].__subclasses__()
[<class '__main__.I'>]

>>> cg.classes["G"].__subclasses__()
[<class '__main__.H'>]

>>> cg.classes["H"].__subclasses__()
[<class '__main__.I'>]

>>> cg.classes["I"].__subclasses__()
[]

Ciao,
Kay




More information about the Python-list mailing list