Re: OO approach to decision sequence?
Lee C - Here is a technique for avoiding the if-elseif-elseif...-else method for building objects. It is a modified form of ChainOfResponsibility pattern, in which you have a collection of factory methods that all have a common signature, or a collection of Factory classes that all implement a makeObject method. These common methods probably take a string, and return a generic object. Fortunately, such type flexibility is *very* easy in Python. :) Example: I want to convert individual strings to native data types. I want to detect integers, reals, complex numbers, and booleans (indicated by 'True' or 'False'). This is kind of similar to a parsing problem, but it could also be used for deserializing or unpickling data of an unknown original type. Note the special treatment I have to go through with boolean values - I needed to write a special makeBool routine, since Python will take any non-empty string to be True, when what I want is 'True' to yield true and 'False' to yield false. Hope this gives you some alternative ideas to your cascading if's. -- Paul def makeBool(s): if s in ('True','False'): return s == 'True' raise ValueError converters = [ int, float, complex, makeBool, str ] def makeObject(stringVar): for conv in converters: try: val = conv(stringVar) except Exception: continue else: break; return val def test(s): val = makeObject(s) print s, val, type(val) test('1') test('1.0') test('1+2j') test('1+0j') test('True') test('False') test('A') prints: 1 1 type 'int' 1.0 1.0 type 'float' 1+2j (1+2j) type 'complex' 1+0j (1+0j) type 'complex' True True type 'bool' False False type 'bool' A A type 'str' -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
Paul McGuire [EMAIL PROTECTED] wrote: Lee C - Here is a technique for avoiding the if-elseif-elseif...-else method for building objects. It is a modified form of ChainOfResponsibility pattern, in which you have a collection of factory methods that all have a common signature, or a collection of Factory classes that all implement a makeObject method. These common methods probably take a string, and return a generic object. Fortunately, such type flexibility is *very* easy in Python. :) Example: I want to convert individual strings to native data types. I want to detect integers, reals, complex numbers, and booleans (indicated by 'True' or 'False'). This is kind of similar to a parsing problem, but it could also be used for deserializing or unpickling data of an unknown original type. Note the special treatment I have to go through with boolean values - I needed to write a special makeBool routine, since Python will take any non-empty string to be True, when what I want is 'True' to yield true and 'False' to yield false. Hope this gives you some alternative ideas to your cascading if's. -- Paul def makeBool(s): if s in ('True','False'): return s == 'True' raise ValueError converters = [ int, float, complex, makeBool, str ] def makeObject(stringVar): for conv in converters: try: val = conv(stringVar) except Exception: continue else: break; return val def test(s): val = makeObject(s) print s, val, type(val) test('1') test('1.0') test('1+2j') test('1+0j') test('True') test('False') test('A') prints: 1 1 type 'int' 1.0 1.0 type 'float' 1+2j (1+2j) type 'complex' 1+0j (1+0j) type 'complex' True True type 'bool' False False type 'bool' A A type 'str' Nice technique. Something that needs to be pointed out is that the order of the converters *is* important; int takes precedence over float, which take precedence over complex and bool takes precedence over string. More succinctly: { int - float - complex } { bool - str } In general the converters will form a strict partially ordered set, so the list of converters should be a topological sort of the respective DAG. George -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
On Sun, 26 Jun 2005 17:58:11 -0400, George Sakkis wrote (in article [EMAIL PROTECTED]): Paul McGuire [EMAIL PROTECTED] wrote: Lee C - Here is a technique for avoiding the if-elseif-elseif...-else method for building objects. It is a modified form of ChainOfResponsibility pattern, in which you have a collection of factory methods that all have a common signature, or a collection of Factory classes that all implement a makeObject method. These common methods probably take a string, and return a generic object. Fortunately, such type flexibility is *very* easy in Python. :) Example: I want to convert individual strings to native data types. I want to detect integers, reals, complex numbers, and booleans (indicated by 'True' or 'False'). This is kind of similar to a parsing problem, but it could also be used for deserializing or unpickling data of an unknown original type. Note the special treatment I have to go through with boolean values - I needed to write a special makeBool routine, since Python will take any non-empty string to be True, when what I want is 'True' to yield true and 'False' to yield false. Hope this gives you some alternative ideas to your cascading if's. -- Paul def makeBool(s): if s in ('True','False'): return s == 'True' raise ValueError converters = [ int, float, complex, makeBool, str ] def makeObject(stringVar): for conv in converters: try: val = conv(stringVar) except Exception: continue else: break; return val def test(s): val = makeObject(s) print s, val, type(val) test('1') test('1.0') test('1+2j') test('1+0j') test('True') test('False') test('A') prints: 1 1 type 'int' 1.0 1.0 type 'float' 1+2j (1+2j) type 'complex' 1+0j (1+0j) type 'complex' True True type 'bool' False False type 'bool' A A type 'str' Nice technique. Something that needs to be pointed out is that the order of the converters *is* important; int takes precedence over float, which take precedence over complex and bool takes precedence over string. More succinctly: { int - float - complex } { bool - str } In general the converters will form a strict partially ordered set, so the list of converters should be a topological sort of the respective DAG. George Ah yes, there is more than one way to skin a cat :~) and your samples are helping me get a better grasp of both Python and OOP in such. I find both Bengt's and Paul's variation on a theme understandable (I must be making progress :~) and interesting. I must admit that I did have to look twice at Bengt's clever little slice on 'an' though. I had already worked my way through a href=http://fraca7.free.fr/blog/index.php?2005/02/28/2-design-patterns-part- i---chain-of-responsibilitythis/a though, so they were that much more understandable. Actually, I'm using a superclass as a factory, similar to Steve B's example (as modified by someone I can't find the name of). The difference is that my criteria does not map easily to class names so I have the verbose decision sequence in my superclass. Of course, if I want to get really fancy, I could use the COR pattern within such. I'm making more hay out of this one little pasture than I expected (i.e. increasing my understanding) thanks to all of you. I'm going to have to start writing down names (rather than rely on this ol head) so I can properly thank everyone - like those clarifying other examples. At some point I'll put up the original top-down and OO refactored versions of my little utility in the hope that I will learn even more from criticism of my efforts. The new deal top-down structured programming encountered in my continuing post grad work at BU in the 1960s was easier to get a solid handle on ;') Much appreciated, Lee C Wonder rather than doubt is the root of knowledge. --Abraham Joshua Heschel -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
Ok, I'm glad you guys liked that design pattern. Here are a few additional footnotes: 1. As George mentions, the order of the converters is *very* important, especially in this particular case. One might question whether '1+0j' would convert to a complex or an int - my first thought was to make it an int, but that made the example more complicated, and I didn't really need to be fussy to make the example. 2. If this were a design for a deserializer, it would be a bit poor, if only because of this order-sensitivity. Also, there is no way to reconstruct a string containing a value that maps to one of the other types, such as 'True' or '1'. A better deserialize/serialize design would support quoted strings so that a string of '1' would not be misinterpreted as an int. But again, I didn't want to take on deconstructing quoted strings, escaped characters, etc. But in general, when designing a deserializer/serializer, it's better to make the output more unambiguous. 3. I once had an O-O expert tell me that this was not really a Chain-of-Responsibility pattern. His take was that, in COR, each link succeeds, or calls the next link in the chain. In my design, each link is just one of a collection, and succeeds or raises an exception; the iteration is done from the calling routine. I really haven't found a name for this pattern, so I just call it modified COR. -- Paul -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
On Sat, 18 Jun 2005 03:52:28 -0400, Brian van den Broek [EMAIL PROTECTED] wrote: [...] Now, the same sort of behaviour where the if type testing has been replaced with code more in keeping with the OOP approach: class C(object): ...def report(self): ...print Found a C ... class D(object): ...def report(self): ...print Found a D ... c = C() d = D() for item in (c, d): ...item.report() ... Found a C Found a D The OP might want to consider factoring report into a base class, e.g., class Base(object): ... def art_name(self): ... cname = type(self).__name__ ... art = 'an'[:1+(cname.upper() in 'A E F H I L M N O R S X' or ... len(cname)1 and cname.upper()[0] in 'AEIOU')] ... return art, cname ... class A(Base): pass ... class B(Base): pass ... class F(Base): pass ... class Foo(Base): pass ... class U(Base): pass ... class Uhuh(Base): pass ... items = A(), B(), F(), Foo(), U(), Uhuh() for item in items: print 'Found %s %s' % item.art_name() ... Found an A Found a B Found an F Found a Foo Found a U Found an Uhuh Returning info rather than printing to stdout allows you to access and use it differently, e.g., items[3].art_name() ('a', 'Foo') items[3].art_name()[1] 'Foo' (Don't know if the a/an logic is really general ;-) Regards, Bengt Richter -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
Chinook wrote: I understand what you are saying. The point I'm messing up my head with though, is when the entity (tree node in my case or variable record content deconstructing in the aspect example I noted) is not an instance of a class already - it is obtained from an external source and only decipherable by its content. There are several aspects and approaches here, depending on the data. If you receive a stream of text, you might want to build a parser to handle that. For instance you might want to use something like PyParsing. That's not really an OO approach, but once you have your objects, it's time for OO! :) Note, that while the basic approach of the factory patterns is applicable to Python, most texts describe how to use it with C++ or Java, and do a large degree, they are complicated by shortcomings in those languages (such as static typing and reliance on inheritence for polymorphism) that are non-issues in Python. IOW, keep it simple. -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
Jordan Rastrick wrote: Without knowing more about your problem, I think the most obvious OO approach would be to write a seperate (simple) class for each of node_type_1, node_type_2, etc. While I agree that this is the cleanest and usually simplest approach, it does have its drawbacks. I'm currently working on a project where I'd very much like to avoid writing a whole set of classes just for the purpose of avoiding a decision chain. For a PDF library, I need basic data types that are used in a PDF document. Such are integers, floats, strings, lists, dictionaries and a few. At some point they have to be written to a file, and at first I was tempted to create types like pdfint, pdffloat, pdfstr etc. which implement the respective file encoding either in a write method or directly in __str__. However, the whole point of the library is to allow working with the document's data. Beside manipulating existing (as in read from a PDF file) mutable objects this includes creating new objects of type pdffoo. And I realized it is very bothersome to have to say x = pdfint(5) instead of x = 5 everytime I deal with integers that would end up in the document. Similar for, e.g., adding to PDF integers: x = pdfint(y+z) instead of just x = y+z. The latter can be cured by touching all methods returning any pdffoo instances. No sane person would do this, however, and it would not eliminate any pdffoo(x) type conversions in the app code anyway. So I decided that in this case it is best to go without special types and use those provided by Python, and live with an ugly decision chain or two at defined places in the library. -- Thomas -- http://mail.python.org/mailman/listinfo/python-list
OO approach to decision sequence?
OO approach to decision sequence? - In a recent thread (Cause for using objects?), Chris Smith replied with (in part): If your table of photo data has several types of photos, and you find yourself saying if is_mugshot: #something elif is_freehand: #something else: #something then OOP will help organize your code. This struck a chord because I'm trying to refactor a top-down approach to an OO approach. The reason I am doing such is to try and get my mind wrapped around OO design, not because the particular module will benefit from an OO approach (it's a simple top-down recursive tree utility). In fact it's probably ill suited for OO and that is why I chose it. I've used an OO approach where I built up record (instance) content in a variable record file, but here I'm trying to come at it from the opposite direction as variable record mapping (deconstructing) would be to such. Anyway, a tree node can be any of seven types where: if node_type_1: # recurse elif node_type_2: # terminus - do something elif node_type_3: # terminus - do something else ... else: # terminus - catch all, do yet something else return #to parent So, where is the magic :~) Seriously, how might OO help me organize this type of problem (alleviate the conventional lengthy if structure)? I've looked at the cookbook, class interface techniques, factory functions and metaclasses till my head is swimming. Am I missing a bolt in the machinery somewhere, or simply trying to find magic that doesn't exist for a straightforward decision sequence? Thank you, Lee C -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
I've coded some simple recursive tree data structures using OO before (unfortunately not in Python though). It's not nessecarily an ill-suited approach to the task, although it depends on the specific details of what you're doing. What's the the piece of code from which your if...elif fragment is taken actually supposed to do? Without knowing more about your problem, I think the most obvious OO approach would be to write a seperate (simple) class for each of node_type_1, node_type_2, etc. Make sure each one provides the same interface, i.e. defines the same set of methods. Then put the different decision branches as implementations of a certain method in each class. class Node_Type_1(object): def recurse(self): # do stuff here class Node_Type_2(object): def recurse(self): # do other stuff here only make sure you use more informative names than Node_Type_1 and recurse :) Your if elif code then collapses to: node.recurse() where the node variables refers to an instance of any one of your Node_Type classes. OO isn't magic. You still in the end have to write the code that implements the decision choices. In this example, its likely the OO code is actually more verbose, since you have all the class and method definitions to write as well. But there are advantages. Adding new cases (new node_types) is simpler and less error prone - you just write a new node_type class. The code for the new class, unlike a new elif brach, is kept cleanly and safely seperate from your existing, correctly working code. If you forget to provide the new class with the recurse method, you get a runtime error. But if you forget to add the case to your if..elif statement, you just end up silently going with your sentinel else branch, which may not be what you really want to do with that node_type. (in fact I'd usually raise an exception on reaching the else branch in any such extended if..elif structure, because it's almost always the result of an error in your program logic) Essentially, although you may end up writing more code, its also better organised, since as you wish it avoids your 'conventional lengthy if struture'. Its a partciularily useful approach if you have several different functions that need to branch on node-type in this fashion (e.g. pretty print the tree, post-order traverse it, run a search over it, etc). For each such function, you just provide a method on every node_type class. Again, if you forget to provide the code for a certain node_type, the error is very easy to detect. If your problem is simple, the OO approach may be overkill - your current if..elif code may be the most straightfoward solution. But if the problem is complicated, or has the potential to grow more complicated, the advantages of the OO approach may be a worthwhile trade off. Fortuantely Python is a multi-paradigm language, so the choice to use or not use OO according to what best suits the design and the problem is left up to you, the programmer, and is not thrust upon you by the language. And you certainly don't want metaclasses or anything else that complex and deep for something like this. Chinook wrote: OO approach to decision sequence? - In a recent thread (Cause for using objects?), Chris Smith replied with (in part): If your table of photo data has several types of photos, and you find yourself saying if is_mugshot: #something elif is_freehand: #something else: #something then OOP will help organize your code. This struck a chord because I'm trying to refactor a top-down approach to an OO approach. The reason I am doing such is to try and get my mind wrapped around OO design, not because the particular module will benefit from an OO approach (it's a simple top-down recursive tree utility). In fact it's probably ill suited for OO and that is why I chose it. I've used an OO approach where I built up record (instance) content in a variable record file, but here I'm trying to come at it from the opposite direction as variable record mapping (deconstructing) would be to such. Anyway, a tree node can be any of seven types where: if node_type_1: # recurse elif node_type_2: # terminus - do something elif node_type_3: # terminus - do something else ... else: # terminus - catch all, do yet something else return #to parent So, where is the magic :~) Seriously, how might OO help me organize this type of problem (alleviate the conventional lengthy if structure)? I've looked at the cookbook, class interface techniques, factory functions and metaclasses till my head is swimming. Am I missing a bolt in the machinery somewhere, or simply trying to find magic that doesn't exist for a straightforward decision sequence? Thank you, Lee C -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
Chinook said unto the world upon 18/06/2005 02:17: OO approach to decision sequence? - In a recent thread (Cause for using objects?), Chris Smith replied with (in part): If your table of photo data has several types of photos, and you find yourself saying if is_mugshot: #something elif is_freehand: #something else: #something then OOP will help organize your code. This struck a chord because I'm trying to refactor a top-down approach to an OO approach. The reason I am doing such is to try and get my mind wrapped around OO design, not because the particular module will benefit from an OO approach (it's a simple top-down recursive tree utility). In fact it's probably ill suited for OO and that is why I chose it. I've used an OO approach where I built up record (instance) content in a variable record file, but here I'm trying to come at it from the opposite direction as variable record mapping (deconstructing) would be to such. Anyway, a tree node can be any of seven types where: if node_type_1: # recurse elif node_type_2: # terminus - do something elif node_type_3: # terminus - do something else ... else: # terminus - catch all, do yet something else return #to parent So, where is the magic :~) Seriously, how might OO help me organize this type of problem (alleviate the conventional lengthy if structure)? I've looked at the cookbook, class interface techniques, factory functions and metaclasses till my head is swimming. Am I missing a bolt in the machinery somewhere, or simply trying to find magic that doesn't exist for a straightforward decision sequence? Thank you, Lee C Hi Lee, I'm a hobbyist who came to grok the OO approach in the last 6 months or so ago. So, take the comments with that in mind. A simple toy example with the if type test approach: class A(object): ... pass ... class B(object): ... pass ... a = A() b = B() for item in (a, b): ... if type(item) == A: ... ident = an A ... if type(item) == B: ... ident = a B ... print Found %s %ident ... Found an A Found a B Now, the same sort of behaviour where the if type testing has been replaced with code more in keeping with the OOP approach: class C(object): ... def report(self): ... print Found a C ... class D(object): ... def report(self): ... print Found a D ... c = C() d = D() for item in (c, d): ... item.report() ... Found a C Found a D A metaphorical explanation that is a bit handwavy, but that I find useful: In both approaches, there is a common behaviour you want the various types of objects to exhibit. (In a less 'toy' example the behaviour would be more complicated.) In the if type style, you are asking each object what kind of object it is, and then setting an aspect of the behaviour as a function of the answer. In the more OOP approach, you rely on the fact that each object knows what kind of object it is. So, you don't need to ask it, and adjust the behaviour accordingly. You just tell it to behave, and, knowing what kind of thing it is, it knows how to behave as befits that kind of thing. Two big benefits in the context are that if you need to exhibit the same behaviour in multiple places, you don't have multiple if type chains, and, if you want to add a type, with its own specific behaviour, you just add a class, and there is no worry about hunting down the if type chains to update them. There was a thread on the tutor list around mid-Feb. which really helped me come to understand the idea. Actually, from Dec. to Feb. or so, there are several long tutor threads where people gave me much useful help coming to see how to employ OOP. Might be worth a trip through the archive. HTH, Brian vdB -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
Jordan Rastrick wrote: I've coded some simple recursive tree data structures using OO before (unfortunately not in Python though). It's not nessecarily an ill-suited approach to the task, although it depends on the specific details of what you're doing. What's the the piece of code from which your if...elif fragment is taken actually supposed to do? Without knowing more about your problem, I think the most obvious OO approach would be to write a seperate (simple) class for each of node_type_1, node_type_2, etc. Make sure each one provides the same interface, i.e. defines the same set of methods. Then put the different decision branches as implementations of a certain method in each class. class Node_Type_1(object): def recurse(self): # do stuff here etc etc and perhaps if you found by the time you got to Node_Type_2 that its opensesame() method was identical to the opensesame() method for Node_Type_1, you might decide that having separate simple classes could be improved on by having a Node class, which you would subclass for each Node_Type_n ... -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
On Sat, 18 Jun 2005 03:52:28 -0400, Brian van den Broek wrote (in article [EMAIL PROTECTED]): Chinook said unto the world upon 18/06/2005 02:17: OO approach to decision sequence? - In a recent thread (Cause for using objects?), Chris Smith replied with (in part): If your table of photo data has several types of photos, and you find yourself saying if is_mugshot: #something elif is_freehand: #something else: #something then OOP will help organize your code. This struck a chord because I'm trying to refactor a top-down approach to an OO approach. The reason I am doing such is to try and get my mind wrapped around OO design, not because the particular module will benefit from an OO approach (it's a simple top-down recursive tree utility). In fact it's probably ill suited for OO and that is why I chose it. I've used an OO approach where I built up record (instance) content in a variable record file, but here I'm trying to come at it from the opposite direction as variable record mapping (deconstructing) would be to such. Anyway, a tree node can be any of seven types where: if node_type_1: # recurse elif node_type_2: # terminus - do something elif node_type_3: # terminus - do something else ... else: # terminus - catch all, do yet something else return #to parent So, where is the magic :~) Seriously, how might OO help me organize this type of problem (alleviate the conventional lengthy if structure)? I've looked at the cookbook, class interface techniques, factory functions and metaclasses till my head is swimming. Am I missing a bolt in the machinery somewhere, or simply trying to find magic that doesn't exist for a straightforward decision sequence? Thank you, Lee C Hi Lee, I'm a hobbyist who came to grok the OO approach in the last 6 months or so ago. So, take the comments with that in mind. A simple toy example with the if type test approach: class A(object): ... pass ... class B(object): ... pass ... a = A() b = B() for item in (a, b): ... if type(item) == A: ... ident = an A ... if type(item) == B: ... ident = a B ... print Found %s %ident ... Found an A Found a B Now, the same sort of behaviour where the if type testing has been replaced with code more in keeping with the OOP approach: class C(object): ... def report(self): ... print Found a C ... class D(object): ... def report(self): ... print Found a D ... c = C() d = D() for item in (c, d): ... item.report() ... Found a C Found a D A metaphorical explanation that is a bit handwavy, but that I find useful: In both approaches, there is a common behaviour you want the various types of objects to exhibit. (In a less 'toy' example the behaviour would be more complicated.) In the if type style, you are asking each object what kind of object it is, and then setting an aspect of the behaviour as a function of the answer. In the more OOP approach, you rely on the fact that each object knows what kind of object it is. So, you don't need to ask it, and adjust the behaviour accordingly. You just tell it to behave, and, knowing what kind of thing it is, it knows how to behave as befits that kind of thing. Two big benefits in the context are that if you need to exhibit the same behaviour in multiple places, you don't have multiple if type chains, and, if you want to add a type, with its own specific behaviour, you just add a class, and there is no worry about hunting down the if type chains to update them. There was a thread on the tutor list around mid-Feb. which really helped me come to understand the idea. Actually, from Dec. to Feb. or so, there are several long tutor threads where people gave me much useful help coming to see how to employ OOP. Might be worth a trip through the archive. HTH, Brian vdB Thanks for the reply Brian. I understand what you are saying. The point I'm messing up my head with though, is when the entity (tree node in my case or variable record content deconstructing in the aspect example I noted) is not an instance of a class already - it is obtained from an external source and only decipherable by its content. In practical terms that leaves me with some decision sequence regardless and I was wondering (from what Chris Smith said) how that might be done in OOP. The whole problem may be that I'm reading too much into what Chris said :~) I will dig back through the Tutor archives as you suggested. Thanks again, Lee C -- http://mail.python.org/mailman/listinfo/python-list
Re: OO approach to decision sequence?
On Sat, 18 Jun 2005 09:10:25 -0400, George Sakkis wrote (in article [EMAIL PROTECTED]): Chinook wrote: I understand what you are saying. The point I'm messing up my head with though, is when the entity (tree node in my case or variable record content deconstructing in the aspect example I noted) is not an instance of a class already - it is obtained from an external source and only decipherable by its content. In practical terms that leaves me with some decision sequence regardless and I was wondering (from what Chris Smith said) how that might be done in OOP. The whole problem may be that I'm reading too much into what Chris said :~) I will dig back through the Tutor archives as you suggested. What you are looking for is what is called the 'factory method pattern' (http://en.wikipedia.org/wiki/Factory_method_pattern) and it's one of the cases where OOP doesn't eliminate the if/elif/elif (or switch in C++/Java). That's ok though because, as you noticed, at some point you have to take a decision. What's important is the once and only once principle, that is all the decision logic is encapsulated in a single method (or in python in a single function) instead of being replicated every time you want to use an existing Node. Regards, George George, Yes, that answers my question of how the issue is approached in OOP - thank you. I'll do some more googling to find examples in Python and then try refactoring my little utility. The effect on my little utility will be a verbose abstraction of a specific efficient top-down approach, but the intended goal is to learn how to better facilitate extensibility. Thanks to all that took the time to wade through my post and especially to those that offered their thoughts, Lee C -- http://mail.python.org/mailman/listinfo/python-list