Re: [Tutor] subclassing across multiple modules
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
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
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
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 =