Associating a dynamic method with inheritance in Python

I am new to python (~ month) and I wish I had switched to it before (after several years of perl).

Problem . I want objects to have different functionality for the same method call based on their type. Methods are assigned at runtime based on a loaded module (which makes up all the objects).

Question I wanted to know if there was a popular design template that I could use instead of the one below, or if it already has a project template name (I, unfortunately, do not have a formal CS background, and knowing that this will help my documentation) ?

I have a class hierarchy (currently 26 of them with 3 base classes). Only base classes have some trivial methods (e.g. add_child), and each derived class only extends the base class with new data attributes (specific to the derived class), overriding methods if necessary (for example: __str __).

I am dealing with a tree (s) where the nodes have different classes. However, nodes must have the same names of certain methods ( stringify , print_tree , emit , eval , transform_X , etc.), thereby allowing the work with an easy / blind iterator. Each method can do something else, but the methods have the same name (for example, polymorphism).

First of all, I wanted to provide specific capabilities (methods) to nodes based on their type. I originally implemented this using a visitors template . But then I realized that I did not need this, given that I worked in Python.

In the example below, I have methods that are dynamically assigned to classes. Note that in the example below, the name of the iteration / recursion method call ( print_tree ) is different from the function name ( generic__print_tree ).

 #astmethods.py def generic__print_tree(self, level=1): """ Desc: print nodes with indentation Target: Any tree node """ print("{}> {}".format('-' * level, self)) for child in self.children: child.print_tree((level + 1)) def ASTNode__stringify(self): """ Desc: Return string representation of the tree under this node Target: AST/CFG nodes """ text = str(self) for child in self.children: text += ", { " + child.stringify() + " }" return text 

Finally, the main modules have this function, extend_types (), which is called during module initialization. Nodes are expected to do different things in the context of this module based on their type (rather than value). Assigned methods are inherited if not overridden.

 # mainModule1.py def extend_types(): """ Upgrade the AST node classes with neat functions for use within this module context """ # same simple functions across class hierarchies # I should just derive them both from a common base class to avoid this ASTNode.print_tree = generic__print_tree SimpleNode.print_tree = generic__print_tree # ASTNode and all derived class get this method ASTNode.stringify = ASTNode__stringify # All AST nodes get the base method, except for Ignore and Arraysel type nodes # traversal looks the same with child.tidy() ASTNode.tidy = ASTNode__tidy ASTIgnore.tidy = ASTIgnore__tidy ASTArraySel.tidy = ASTArraySel__tidy # All AST nodes get the base method, except for the Coverage and If type nodes ASTNode.transform_controlFlow = ASTNode__transform_controlFlow ASTCoverage.transform_controlFlow = ASTCoverage__transform_controlFlow ASTIf.transform_controlFlow = ASTIf__transform_controlFlow 

edit: remote distracting information, made an example for the context of one module

0
python design-patterns visitor
source share
1 answer

Problem Summary

Ignoring irrelevant details, the problems here can be summarized as follows:

There is one base class and many derived classes. There is a certain functionality that should be applied to all derived classes, but depends on some kind of external switch (in the question: choosing the " main module").

The idea in this matter is to make the monkeypatch base class dependent on the switch.

Decision

Instead, you should separate the functionality that depends on the external switch.

In the example:

 # There is a base class: class ASTNode(object): pass # There are many derived classes, eg: class ASTVar(ASTNode): pass # One implementation of function stringify or another # should be defined for all objects, depending on some external factor def stringify1(node): # something def stringify2(node): # something else # Depending on whatever, choose one of them: stringify = stringify1 

Now it is used a little differently than in the source code: instead of node.stringify() now there is stringify(node) . But there is nothing wrong with that.

BTW ...

Perhaps it would be more pleasant to see the class:

class NodeHandler1 (object): def print_tree (node): # do something

 def stringify(node): # do something ... 

But this is not required at all.

Morality

Not a monkey. This is always a bad design.

0
source share

All Articles