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
,因为这很容易忘记,并且像地狱一样难看。
(值得注意的是,在许多情况下,实际上在这个特定的例子中,不可能简单地在父类上定义所有可能的方法,因为这样做可能会产生副作用。)
答案 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__
方法,以便动态地进行查找。