In python you often find a slightly modified version of the classic Visitor Pattern. Instead of using a accept(self, visitor)method in the target class to tell the visitor what kind of object to handle, the accept method is replaced with introspection in the visitor class:
An Extrinsic Visitor implements double dispatch with run time type information instead of Accept() methods. With the same machinery it is possible to test the feasibility of a particular visit before performing its operation. The extrinsic visitor pattern provides several benefits in ease of software development in trade for poorer run time performance. [Variations on the Visitor Pattern - Martin Nordberg]
But the implementation as shown in The Visitor Pattern Revisited has the drawback to not support inheritance:
There is one drawback to this approach, however. As commonly coded in Python, the Extrinsic Visitor pattern does not have any way to deal with inheritance. For example, let's say your PrettyPrinter class has a visit_list() method. And somebody subclasses list to make MyList. You're going to have to add a visit_MyList() to your PrettyPrinter class, even though in all probability the visit_list() method would work just fine. [The Visitor Pattern Revisited]The following code shows a solution supporting inheritance using __mro__, the Python Method Resolution Order:
class Node(object): pass class A(Node): pass class B(Node): pass class C(A,B): pass class Visitor(object): def visit(self, node, *args, **kwargs): meth = None for cls in node.__class__.__mro__: meth_name = 'visit_'+cls.__name__ meth = getattr(self, meth_name, None) if meth: break if not meth: meth = self.generic_visit return meth(node, *args, **kwargs) def generic_visit(self, node, *args, **kwargs): print 'generic_visit '+node.__class__.__name__ def visit_B(self, node, *args, **kwargs): print 'visit_B '+node.__class__.__name__ a = A() b = B() c = C() visitor = Visitor() visitor.visit(a) visitor.visit(b) visitor.visit(c)
If you run the code you get the following output:
generic_visit A visit_B B visit_B C
For the class A the visitor falls back to the generic_visit method because there is no special implementation. For class B the visit_B method is used. And for class C the implementation walks up the __mro__ to find a suitable method from a superclass B visit_B.