Re: [Tutor] subclassing across multiple modules

2005-03-18 Thread Kent Johnson
Brian van den Broek wrote:
Kent Johnson said unto the world upon 2005-03-17 20:44:

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?
No. Some people would agree with that - e.g. the creators of Java - but I 
have seen many times where
MI is useful. Usually it is to combine a primary base class with a mixin - a 
class that adds
functionality orthogonal to that of the base class, or that modifies a 
carefully chosen set of base
class methods.
http://c2.com/cgi/wiki?MultipleInheritanceIsNotEvil
http://c2.com/cgi/wiki?MixIn
For example in a GUI toolkit you might have an Observable add-in that gives a 
class means to
remember and broadcast to listeners. In the Python library there are 
SocketServer mixins that create
a threaded server.
BUT there are some clear gotchas to MI. They mostly arise when you inherit from 
classes that
themselves have a common base class. The diamond pattern is the usual example:
class A:
  ..
class B(A):
  ..
class C(A):
  ..
class D(B, C):
  ..
Now you can have all kinds of fun (i.e. subtle bugs and confusion) if A, B, C 
and D have methods in
common and if they want to call the methods of their base classes. One of the 
changes is new-style
classes (the addition of super() and a change to the method resolution order) 
was made to address
some deficiencies in how old-style classes handle this situation.
So, calling this a code smell is probably not the right term. It's more like a 
red warning light and
klaxon :-) You just don't want to do this without understanding the issues.
See 
http://www.python.org/doc/2.2.3/whatsnew/sect-rellinks.html#SECTION00033
 and PEP
253 for details or google diamond multiple inheritance

I'll google, but my only grasp of the meaning of helper class is what 
comes from intuition.
For example, instead of subclassing Toolkit.Node, could you define the 
extra methods in another
class. Then your NodeX subclasses can inherit from Toolkit.NodeX and 
NodeHelper. e.g.
class MyNodeHelper:
  def doSomethingSpecial(self):
# does something with a Toolkit.Node
class MyNode1(Toolkit.Node1, MyNodeHelper):
  ..
This is an example of a mixin class.
Kent
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


[Tutor] subclassing across multiple modules

2005-03-17 Thread Brian van den Broek
Hi all,
I'm uncertain of how to make use of OOP design across multiple 
modules. (Actually, on reflection, I'm not certain the multiple 
modules aspect is central.) I'm also uncertain of how to frame the 
question well, so please bear with me :-)

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)
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.

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

So, how can I make the lines like
node = Node1(chunk_contents)
of the Toolkit.Tree.parser method say something that means create a 
Node1 instance where Node1 is as defined in the module that defines 
the instantiated Tree class so that an instance of Application.Tree 
creates instances of Application.Node1? Is that doable? Or, is some 
other design indicated?

Thanks for making it to the end of the question! :-)
Best to all,
Brian vdB
___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


[Tutor] subclassing across multiple modules

2005-03-17 Thread Lloyd Kvam
You want a method in a base class to parse input and create instances of
certain derived classes.  Your sample code looks like:
if some_condition_on_chunk_contents:
 node = Node1(chunk_contents)
else:
 node = Node2(chunk_contents)

I'd suggest changing the method to use a variable to determine the
class.  Following your pattern of coding:
#Toolkit.py
class Tree:
node1 = Node1
node2 = Node2
...
class Node1(Node):
...

#Application.py
class Tree(Toolkit.Tree):
node1 = Node1
node2 = Node2
...
class Nodes(Node,Toolkit.Node1):
...

if some_condition_on_chunk_contents:
 node = self.node1(chunk_contents)
else:
 node = self.node2(chunk_contents)

This requires that the class constructors be similar enough so that they
take the same parameters.  It is also a manual effort to keep the node
assignments in Tree in sync with the Node classes that you write.  You
also face a similar issue keeping Application.py and Toolkit.py in sync.

-- 
Lloyd Kvam
Venix Corp

___
Tutor maillist  -  Tutor@python.org
http://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] subclassing across multiple modules

2005-03-17 Thread Brian van den Broek
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 =