[Tutor] subclassing across multiple modules
Brian van den Broek
bvande at po-box.mcgill.ca
Fri Mar 18 06:40:03 CET 2005
Kent Johnson said unto the world upon 2005-03-17 20:44:
> Brian van den Broek wrote:
>
>> A schematic of what I have (with fake names for ease of example) is a
>> base module Toolkit.py and I want to write a module Application.py
>> which specializes the behaviour of the Toolkit.py classes. (I'm using
>> old-style classes, but don't feel committed to that choice.)
>>
>> Toolkit.py defines:
>>
>> class Tree
>> class Node
>> class Node1(Node)
>> class Node2(Node)
>> (other Node subclasses, too, but I'm simplifying.)
>>
>> The Tree class contains a parser method to parse a file. It reads the
>> file, breaking it into chunks, and for each chunk, does the following:
>>
>> if some_condition_on_chunk_contents:
>> node = Node1(chunk_contents)
>> else:
>> node = Node2(chunk_contents)
>>
>> self.nodes.append(node)
>>
>>
>> Application.py will define
>>
>> class Tree(Toolkit.Tree)
>> class Node(Toolkit.Node)
>> class Node1(Node, Toolkit.Node1)
>> class Node2(Node, Toolkit.Node2)
>
>
> You're asking for trouble using the same name. Do something like
> class MyTree(Toolkit.Tree)
> class MyNode(Toolkit.Node)
> class MyNode1(MyNode, Toolkit.Node1)
> class MyNode2(MyNode, Toolkit.Node2)
Hi all,
thanks for the reply, Kent.
I spent a few minutes trying to recall why I had used all the same
names in the first place. Then it hit me: it was because I hadn't
worked out that the issue I posted about today was there -- I'd
naively thought that if I used the same names, I would seamlessly get
that a method of an Application.Tree instance that was inherited from
Toolkit.Tree would `point' to Application.Node, etc. Oh, well. :-)
> The multiple inheritance from MyNode and Toolkit.NodeX is a smell. I
> guess you do this because you want to override methods of Toolkit.Node
> as well as Toolkit.NodeX, or add methods to both MyNode1 and MyNode2? I
> would look for another way to do this, maybe using some kind of helper
> class to hold some common functions?
The adding methods rationale. I never instantiate Node, instead I use
it for behaviour common to Node1 and Node2. Toolkit.py gives
specialized methods to both Node1 and Node2. I need MyNode1 and
MyNode2 (the Application.py versions) to have all the powers of the
Toolkit.Node1 and Node2 classes, plus some new powers in common, and
some new powers defined separately. That is the sort of thing I had
understood multiple inheritance to be for -- but my understanding is
about as shaky as an addict in withdrawal :-)
Are you suggesting the multiple inheritance is always a smell?
I'll google, but my only grasp of the meaning of "helper class" is
what comes from intuition.
>> In all cases, a few methods will be added. I had no plans to override
>> existing methods.
>>
>> My problem is that I want Application.Tree.parser to create
>> Application.Node1 and Application.Node2 instances when parsing a file.
>> From testing around with simpler cases, it seems as though unless I
>> override Toolkit.Tree.parser in Application.Tree, the inherited
>> Tree.parser method will create Toolkit.Node1 and Node2 objects, which
>> isn't what I want.
>>
>> Toolkit.Tree.parser is my longest Tree method, and I definitely think
>> it would be bad to just copy and paste the method def into
>> Application.Tree so make the node = Node1(), etc. lines create an
>> Application.Node1, etc. object.
>
>
> Right, you don't want to do this.
>
>> The best I have come up with is to replace the Toolkit.Tree.parser
>> lines of the form:
>> node = Node1(chunk_contents)
>> with lines like
>> node = self.get_Node1(chunk_contents)
>>
>> and then have *both* Toolkit.Tree and Application.Tree define methods:
>>
>> def get_Node1(self, chunk_contents):
>> return Node1(chunk_contents)
>>
>> (like lines and methods for Node2)
>>
>> This works in my test case, but still involves Copy and Paste of the
>> identical methods get_Node1 (and get_Node2) in both modules. Smelly. :-P
>
>
> This is actually good design. It is a simple example of the Template
> Method pattern. Use Template Method when you have a function that
> defines the outline of an algorithm, but you need to specialize some
> part of the algorithm. You wrap the function in a class (you already
> have this since it is a method) and call helper methods for the parts
> you want to specialize. Then subclasses override the helper methods.
>
> The methods in MyTree will be different from the ones in Toolkit.Tree,
> they will look like
> def get_Node1(self, chunk_contents):
> return MyNode1(chunk_contents)
Bing! Since I'd had the homonymously named class in the first place, I
thought this approach would be a smell. (Don't repeat yourself,
because then you keep saying the same thing, and repetition is
redundant in that it conveys the same information more than once and
is pleonastic, as well as overly verbose. ;-) )
But the smell was elsewhere :-)
> A couple of alternatives:
>
> You could actually pass the class constructors directly to
> Tree.parser(). It would look like this:
>
> def parser(self, makeNode1, makeNode2): # makeNode1 & 2 are callables
> that return nodes
> # ...
> if some_condition_on_chunk_contents:
> node = makeNode1(chunk_contents)
> else:
> node = makeNode2(chunk_contents)
>
> Then at the point of call you have
> tree.parser(MyNode1, MyNode2) # constructors are callable
>
> There are several variations on this depending on the details of your
> requirements. You could pass makeNode1 and makeNode2 to the Tree
> constructor and save them as instance variables. You could have a
> separate class with makeNode1() and makeNode2() methods and pass an
> instance of that to parse() or Tree.__init__()...
That gives me much to play with. Thanks :-)
Best to all,
Brian vdB
More information about the Tutor
mailing list