Шаблон посетителя в python
вот упрощенная реализация шаблона посетителя в C++. Возможно ли реализовать что-то подобное в 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 );
}
4 ответов
шаблон посетителя может быть реализован в Python, я использую его для реализации чистого интерфейса между моими данными и уровнем презентации. Уровень данных может определять порядок данных. и слой презентации просто печатает / форматирует его:
в моем модуле данных у меня есть:
class visited(object):
....
def accept(self, visitor):
visitor.visit(self)
for child in self.children():
child.accept(visitor)
class typeA(visited):
....
все мои классы данных наследуются от этого посещаемого класса, а посещаемый класс также предоставляет некоторые простые функции для базовых данных, которые нужны всем моим объектам, например name, parent и т. д., и методы управления дочерним списком-который предоставляется 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
и возврат, и объект посетителя знает, какой класс данных использовать.
вы можете использовать декораторы, чтобы получить то, что вы хотите. Копирование примера из этого блог:
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!"
и код @гость оформителя (в случае, если ссылка мертва):
# 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
к сожалению, вышеупомянутое решение не работает для версий python ниже 3.3, поскольку методы по-прежнему являются регулярными функциями при передаче декоратору. Вы можете попробовать использовать как класс, так и метод decorator, см. может ли декоратор Python метода экземпляра получить доступ к классу?.
вы может реализовать это в 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'>
в случае, если кто-то находит это полезным, я получил следующую версию Joren по @visitor
работа с использованием интроспекции в 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