2014-09-17 1 views
8

вот упрощенная реализация шаблона посетителя на C++. И что можно реализовать в Python что-то подобное?Шаблон посетителя в python

Мне это нужно, потому что я передам объекты из кода C++ функции в Python. Моя идея состояла в том, чтобы реализовать посетителя в Python, чтобы узнать тип объекта.

Мой C++ код:

#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); 
} 

ответ

12

Паттерн посетитель может быть реализован в Python, я использую его, чтобы реализовать чистый интерфейс между моим данных и уровня представления. Уровень данных может определять порядок данных. и презентация слой просто печатает/форматы:

В моем модуле данных у меня есть:

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

class typeA(visited): 
    .... 

Все мои классов данных наследуют от этого посещало занятие, а также посетил класс также предоставляют некоторые простые функции для основных данные, которые мне нужны, например, имя, родительский элемент и т. д., а также методы управления дочерним списком - который подвергается методу children(), использованному выше. каждый из подкласс будет строить свои собственные данные, иметь свои собственные свойства и, возможно, даже свой собственный дочерний класс, которые добавляются в список детей, поддерживаемый посещенным суперклассом.

Мой класс посетитель так:

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""" 
      ... 

_find_data_instance некоторый код, который строит или находит экземпляр одного из моих экземпляров данных. В моем случае все мои классы данных имеют конструктор, который принимает objectId и возвращает, а объект-посетитель знает, какой класс данных использовать.

+0

Благодарим за помощь. – bogdan

+0

Не знаете, какую версию Python вы используете, но в версии 2.7.6 вызов 'instance' не существует. Вы должны использовать 'isinstance' – Matthias

+0

@Matthias - хороший catch - были бы рады, если бы вы включили это как предложение редактирования –

3

Вы могли реализовать это в Python, но на самом деле нет необходимости. Python - динамический, интерпретируемый язык, что означает, что информация о типе легко доступна во время выполнения.

Так что ваш пример выше может быть столь же просто, как

class C1(object): 
    pass 

class C2(object): 
    pass 

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

который даст:

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

Благодарим за помощь! Я бы принял ваш ответ, но Тони был немного более подробным. – bogdan

8

Вы можете использовать декораторы, чтобы получить то, что хотите. Копирование примера из этого 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!" 

и кода для @visitor декоратора (в случае, если ссылка идет мертва):

# 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 

Связанные блог (первый один уже, кажется, вниз): https://chris-lamb.co.uk/posts/visitor-pattern-in-python

EDIT:

obj.__qualname__ не доступен до Python 3.3, так что мы должны использовать хак для более низких версий: -

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 

К сожалению, выше решение не работает для версий питона ниже 3.3, а методы все еще регулярные функции при передаче в декоратор. Вы можете попробовать использовать как декоратор класса, так и метод, см. Can a Python decorator of an instance method access the class?.