How to invoke super when the parent method may not be defined?
Question: Certain classes in the Python standard library (and more generally) make use
of dynamic dispatch to call specialised methods in subclasses.

For example, the
[`ast.NodeVisitor`](https://docs.python.org/3/library/ast.html) class defines
a `visit` method. This method calls `visit_classname` methods where
appropriate. These methods are not defined on `ast.NodeVisitor` itself, but
may be provided by interested subclasses.

In other words, subclasses override only the methods that they wish to handle,
eg:

    
    
    class SpecialNodeVisitor(ast.NodeVisitor):
        def visit_FunctionDef(self, node):
            print(node)  # prints any node of type FunctionDef
    

Things get more complicated if `SpecialNodeVisitor` is itself subclassed.
`super()` may be used if `visit_FunctionDef` is overriden, but not in other
cases, ie:

    
    
    class EvenMoreSpecialNodeVisitor(SpecialNodeVisitor):
        def visit_FunctionDef(self, node):
            super().visit_FunctionDef(node)  # works fine
            # ...
    
        def visit_Call(self, node):
            super().visit_Call(node)  # AttributeError
            # ...
    

Specifically, the second example causes `AttributeError: 'super' object has no
attribute 'visit_Call'`.

* * *

The above behaviour makes sense: the parent class _doesn't_ have the method in
question. However, it causes two problems:

  * When writing the subclass, some dynamic methods need to call `super()`, but some don't. This inconsistency makes it _really_ easy to make mistakes.
  * If a new dynamic method is later added to the parent class, all of the subclasses have to be altered to call `super()`. This breaks a really fundamental rule of object-oriented programming.

**Ideally, all subclass methods should be able to make use of`super()`, with
the call being a no-op if the method is not defined. Is there a 'pythonic' way
to achieve this?**

I am particularly after a solution which is transparent to the subclass (for
example, I don't want to try/except on `AttributeError` in every single
method, as this would be just as easy to forget, and is ugly as hell).

(It's worth noting that in many cases, and indeed in this particular example,
it's not possible to simply define all the possible methods on the parent
class, as doing so may have side effects.)


Answer: You cannot have what you want; the most readable method is to simply use
`try..except` on that `AttributeError`:

    
    
    def visit_Call(self, node):
        try:
            super().visit_Call(node)
        except AttributeError:
            pass
    

The alternative would be for you to add aliases for
`NodeVisitor.generic_visit` for every node type to `SpecialNodeVisitor`:

    
    
    import inspect
    
    class SpecialNodeVisitor(ast.NodeVisitor):     
        def visit_FunctionDef(self, node):
            print(node)  # prints any node of type FunctionDef
    
    _ast_nodes = inspect.getmembers(
        ast,
        lambda t: isinstance(t, type) and issubclass(t, ast.AST) and t is not ast.AST)
    for name, node in _ast_nodes:
        name = 'visit_' + name
        if not hasattr(SpecialNodeVisitor, name):
            setattr(SpecialNodeVisitor, name, ast.NodeVisitor.generic_visit)
    

You could encapsulate that into a meta class if you want to. Since `super()`
looks directly into the class `__dict__` namespaces you cannot simply define a
`__getattr__` method on the meta class to do the lookup dynamically,
unfortunately.

