如果不能定义父方法,如何调用super?

时间:2014-12-24 13:07:06

标签: python inheritance super

Python标准库中的某些类(更一般地说)使用动态调度来调用子类中的专用方法。

例如,ast.NodeVisitor类定义了visit方法。 此方法在适当的地方调用visit_classname方法。 这些方法没有在ast.NodeVisitor本身定义,但可能由感兴趣的子类提供。

换句话说,子类只覆盖他们希望处理的方法,例如:

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

如果SpecialNodeVisitor本身是子类,事情会变得更复杂。 如果super()被覆盖,则可以使用visit_FunctionDef,但在其他情况下则不会,例如:

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

    def visit_Call(self, node):
        super().visit_Call(node)  # AttributeError
        # ...

具体来说,第二个例子导致AttributeError: 'super' object has no attribute 'visit_Call'


上述行为是有道理的:父类有问题的方法。 但是,它会导致两个问题:

  • 编写子类时,一些动态方法需要调用super(),但有些则不需要。这种不一致使得真的容易犯错误。
  • 如果稍后将新的动态方法添加到父类,则必须更改所有子类以调用super()。这打破了面向对象编程的真正基本规则。

理想情况下,所有子类方法都应该能够使用super(),如果未定义方法,则调用为无操作。 是否有“pythonic”方法来实现这一目标?

我特别关注对子类透明的解决方案(例如,我不想在每个方法中尝试/除AttributeError,因为这很容易忘记,并且像地狱一样难看。

(值得注意的是,在许多情况下,实际上在这个特定的例子中,不可能简单地在父类上定义所有可能的方法,因为这样做可能会产生副作用。)

1 个答案:

答案 0 :(得分:1)

你不能拥有你想要的东西;最易读的方法是在try..except上使用AttributeError

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

替代方案是为NodeVisitor.generic_visit为每个节点类型添加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)

如果您愿意,可以将其封装到元类中。由于super()直接查看类__dict__命名空间,因此您不能简单地在元类上定义__getattr__方法,以便动态地进行查找。