Lee, Interesting idea, but I think the technique of "inherit from MF to automatically add class to the test chain" is a gimmick that wont scale.
Here are some things to consider: - I'm not keen on the coupling of forcing your A,B,etc. classes to inherit from MF. Especially in a duck-typing language like Python, it adds no value, the subclasses receive no default behavior from their superclass, and I'm not keen on using the inheritance hierarchy to register test classes. What is the order of classes returned from __subclasses__()? Will you always want this order? Will you always want all subclasses? If this is part of a library that others will use, you may not be able to anticipate what subclasses someone else may stick on to your MF class. - The list of items should be dynamic in the calling code, not built statically by your class structure. There's no way to anticipate what various sequences you will want to evaluate. - Let's call the MF class "MultiEvaluator". There are several ways to evaluate among several alternatives: . short-circuit, take first match . best-fit, evaluate all and take best score (assuming the testit routines return a double or int, as opposed to a bool) For short-circuiting, say you have a case that will match A, you want to avoid any processing related to B,C,etc. as possible at test/do runtime. You *might* be able to do extra work at list construction time. Consider these two alternative designs: class MultiEvaluator(object): def __init__(self, *classes): self.testClasses = classes[:] def findit(self, *args): for C in self.testClasses: testobj = C() if testobj.testit(args[0]): testobj.doit() MultiEvaluator(A,B).findit("relates to A") vs. class MultiEvaluator(object): def __init__(self, *classes): self.testClasses = [ C() for C in classes ] def findit(self, *args): for testobj in self.testClasses: if testobj.testit(args[0]): testobj.doit() MultiEvaluator(A,B).findit("relates to B") In the first case, no B class is ever constructed, so if test object construction is expensive, you might go this route. In the second, A() and B() are built ahead of time, so the run-time processing is the fastest - you might choose this if the construction time can be done up front. The second option may cause problems for multi-threadedness or re-entrancy with a shared MultiEvaluator, though. This style of constructing the MultiEvaluator also makes more explicit (which I hear is better than implicit) what classes you are testing when creating your MultiEvaluator. - To make your testit() and doit() code more flexible and more powerful, I'd pass (*args) to each, as in: class MultiEvaluator(object): def __init__(self, *classes): self.testClasses = classes[:] def findit(self, *args): for C in self.testClasses: testobj = C() if testobj.testit(args): testobj.doit(args) - In the interests of flogging OO-ness, you could make MultiEvaluator into a function object, by changing "findit" to "__call__". Then your invocation of it would change from: getObj = MultiEvaluator(A,B) getObj.findit("relates to B") to: getObj = MultiEvaluator(A,B) getObj("relates to B") Although some might claim this is *less* explicit. The purpose of this is that you could then pass getObj to functional routines like map (although you could also pass getObj.findit). Those are my comments off the top of my head. Let us know what you come up with. -- Paul -- http://mail.python.org/mailman/listinfo/python-list