Visitor pattern in python

Related searches

here is a simplified implementation of the visitor pattern in C++. Ist it possible to implement something like this in Python?

I need it, because I will pass Objects from C++ code to a function in Python. My idea was to implement a visitor in Python to find out the type of the Object.

My C++ code:

#include <iostream>
#include <string>


class t_element_base
{
public:
    virtual void accept( class t_visitor &v ) = 0;
};


class t_element_deriv_one: public t_element_base
{
public:
    void accept( t_visitor &v );

    std::string t_element_deriv_one_text()
    {
        return "t_element_deriv_one";
    }
};


class t_element_deriv_two: public t_element_base
{
public:
    void accept( t_visitor &v );

    std::string t_element_deriv_two_text()
    {
        return "t_element_deriv_one";
    }
};


class t_visitor
{
public:
    void visit( t_element_deriv_one& e ){ std::cout << e.t_element_deriv_one_text() << std::endl; }
    void visit( t_element_deriv_two& e ){ std::cout << e.t_element_deriv_two_text() << std::endl; }
};


void t_element_deriv_one::accept( t_visitor &v )
{
    v.visit( *this );
}

void t_element_deriv_two::accept( t_visitor &v )
{
    v.visit( *this );
}


int
main
(
void
)
{
    t_element_base* list[] =
    {
        new t_element_deriv_one(), new t_element_deriv_two()
    };
    t_visitor visitor;

    for( int i = 0; i < 2; i++ )
        list[ i ]->accept( visitor );
}

The visitor pattern can be implemented in Python, I use it to implement a clean interface between my data and presentation layer. The data layer can determine the ordering of the data. and the presentation layer simply prints/formats it :

In my data module I have :

 class visited(object):
     ....
     def accept(self, visitor):
         visitor.visit(self)
         for child in self.children():
             child.accept(visitor)

 class typeA(visited):
    ....

All of my data classes inherit from this visited class, and the visited class also exposes some simple functions for basic data all my objects need e.g. name, parent, etc, and methods for managing the child list - which is exposed by the children() method used above. each of the sub class will build their own data, have their own properties and maybe even their own child class - which get added to the children list maintain by the visited super class.

My visitor class is like this :

class visitor(object):
      def __init__(self, obj_id):
          data_obj = _find_data_instance( obj_id )
          data_obj.accept(self)

      def visit( self, data_obj):
          if isinstance(data_obj, typeA):
               self.visit_typeA( dataobj)

      def visit_typeA(self, dataobj):
          """Formats the data for typeA"""
          ...

the _find_data_instance is some code that builds or finds a instance of one of my data instances. In my case all of my data classes have a constructor which takes a objectId and return, and the visitor object knows what data class to use.

Design Patterns: Visitor in Python, Visitor is a behavioral design pattern that allows adding new behaviors to existing class hierarchy without altering any existing code. Read why Visitors can't be simply replaced with method overloading in our article Visitor and Double Dispatch. The design pattern that solves this kind of problem is called a “visitor” (the final one in the Design Patterns book), and it builds on the double dispatching scheme shown in the last section. The visitor pattern allows you to extend the interface of the primary type by creating a separate class hierarchy of type Visitor to virtualize the

You can use decorators to get what you want. Copying an example from this blog:

class Lion: pass
class Tiger: pass
class Bear: pass

class ZooVisitor:
    @visitor(Lion)
    def visit(self, animal):
        return "Lions"

    @visitor(Tiger)
    def visit(self, animal):
        return "tigers"

    @visitor(Bear)
    def visit(self, animal):
        return "and bears, oh my!"

animals = [Lion(), Tiger(), Bear()]
visitor = ZooVisitor()
print(', '.join(visitor.visit(animal) for animal in animals))
# Prints "Lions, tigers, and bears, oh my!"

and the code for the @visitor decorator (in case the link goes dead):

# A couple helper functions first

def _qualname(obj):
    """Get the fully-qualified name of an object (including module)."""
    return obj.__module__ + '.' + obj.__qualname__

def _declaring_class(obj):
    """Get the name of the class that declared an object."""
    name = _qualname(obj)
    return name[:name.rfind('.')]

# Stores the actual visitor methods
_methods = {}

# Delegating visitor implementation
def _visitor_impl(self, arg):
    """Actual visitor method implementation."""
    method = _methods[(_qualname(type(self)), type(arg))]
    return method(self, arg)

# The actual @visitor decorator
def visitor(arg_type):
    """Decorator that creates a visitor method."""

    def decorator(fn):
        declaring_class = _declaring_class(fn)
        _methods[(declaring_class, arg_type)] = fn

        # Replace all decorated methods with _visitor_impl
        return _visitor_impl

    return decorator

Related blog (first one already seems to be down): https://chris-lamb.co.uk/posts/visitor-pattern-in-python

EDIT:

obj.__qualname__ isn't available until Python 3.3, so we have to use a hack for lower versions:-

def _qualname(obj):
    """Get the fully-qualified name of an object (including module)."""

    if hasattr(obj, '__qualname__'):
        qualname = obj.__qualname__
    else:
        qualname = str(obj).split(' ')[1]

    return obj.__module__ + '.' + qualname

Unfortunately the above solution doesn't work for python versions below 3.3, as methods are still regular functions when passed to a decorator. You can try using both a class - and method decorator, see Can a Python decorator of an instance method access the class?.

Visitor — Python 3 Patterns, Recipes and Idioms, Visitor�. The visitor pattern is implemented using multiple dispatching, but people often confuse the two, because they look at the implementation rather than the� The visitor pattern can be implemented in Python, I use it to implement a clean interface between my data and presentation layer. The data layer can determine the ordering of the data. and the presentation layer simply prints/formats it :

You could implement this in Python, but there's really no need to. Python is a dynamic, interpreted language, which means that type information is easily available at runtime.

So your above example could be as simple as

class C1(object):
    pass

class C2(object):
    pass

l = [C1(), C2()]
if __name__=="__main__":
    for element in l:
        print type(element)

which will yield:

<class '__main__.C1'>
<class '__main__.C2'>

Visitor Design Pattern in Python Back to Visitor description """ Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. """ import abc class Element(metaclass=abc.ABCMeta): """ Define an Accept operation that takes a visitor as an argument.

Visitor Method – Python Design Patterns Visitor Method is a Behavioral Design Pattern which allows us to separate the algorithm from an object structure on which it operates. It helps us to add new features to an existing class hierarchy dynamically without changing it.

In case someone finds it useful, I got the following version of Joren's answer @visitor working using introspection in Python 2:

_visitors = {}

def visitor(arg_type):
    "A @visitor decorator"
    def decorated(fn):
        import inspect
        stack = inspect.currentframe()
        class_name = stack.f_back.f_code.co_name
        full_name = fn.__module__ + '.' + class_name + '.' + fn.__name__
        _visitors[(full_name, arg_type)] = fn

        def _visitor_impl(self, arg, *rest, **kwargs):
            full_name = fn.__module__ + '.' + self.__class__.__name__ + '.' + fn.__name__
            assert (full_name, arg.__class__) in _visitors, "Can't find visitor in {} for {}".format(full_name, arg.__class__.__name__)
            method = _visitors[(full_name, arg.__class__)]
            return method(self, arg, *rest, **kwargs)

        return _visitor_impl

    return decorated

The visitor pattern is tremendously useful when working with certain kinds of information like abstract syntax trees. It's basically a poor man's version of sum types for languages that don't natively support them. Unfortunately, they take advantage of function overloading, something which duck-typed languages like Python lack.

In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures.

In Visitor pattern, we use a visitor class which changes the executing algorithm of an element class. By this way, execution algorithm of element can vary as and when visitor varies. This pattern comes under behavior pattern category. As per the pattern, element object has to accept the visitor

Visitor pattern in C++. Full code example in C++ with detailed comments and explanation. Visitor is a behavioral design pattern that allows adding new behaviors to existing class hierarchy without altering any existing code.

Comments
  • Thank you for your help.
  • Don't know which Python version you use, but in version 2.7.6 the instance call does not exists. You should use isinstance
  • @Matthias - good catch - would have been happy for you to put that in as an edit suggestion
  • Thank you for your help! I would have accepted your answer, but Tony's was a little more detailed.