Steve Howell wrote:
My objection to the interface you describe is that Node defines the type of operations that can be done to it by third-party code, which is something that I cannot predict
I think you have the right idea with a mapping from node classes to implementations of operations, but your intermediate classes SumPrettyPrintNode etc. are not necessary. Just put functions implementing the operations directly into the mapping. Another thing is that the mappings should be somewhere global that can be extended, perhaps through a registration interface. Putting the mapping dicts inside the node classes doesn't gain you anything over simply using methods. All you've done is reimplement method dispatch. A third thing is that instead of just looking up the node class in the mapping, you may want to walk up the MRO and try each of the base classes until you find a match. That way, nodes will effectively inherit implementations from their base classes for any operations that they don't explicitly implement. This inheritance behaviour becomes important if you have two developers A and B, where A adds new classes and B adds new operations. If A and B don't talk to each other, you necessarily end up with gaps in the matrix: A's classes won't have implementations for B's operations. The best you can hope for is that the missing operations will somehow fall back gracefully on default implementations. Inheritance allows this to be achieved: if A derives all his classes from existing node classes, and B provides default implementations of all his operations for the root Node class, then some implementation will always be found for every class/operation combination. Now, there's actually a very easy way of implementing all this. Your registration interface could simply be def register_operation(klass, operation_name, function): setattr(klass, operation_name, function) In other words, monkey-patch the classes to add the new functions directly as methods. Since it's so simple, you could even do away with the registration function altogether and just have developers insert methods directly into the classes. Some people might consider this an ugly hack, but the end result is almost exactly the same, it's very simple, and it's very efficient, making use of Python's existing method dispatch machinery instead of reinventing it yourself. One reason you might want to keep the registration function, even if all it's doing is modifying the classes, is to make it easier to find where operations are being defined, by searching for calls to register_operation(). -- Greg -- http://mail.python.org/mailman/listinfo/python-list