这是C ++中访问者模式的简化实现。我有可能在Python中实现这样的东西吗?
我需要它,因为我会将对象从C ++代码传递给Python中的函数。我的想法是用Python实现一个访问者来找出Object的类型。
我的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 );
}
答案 0 :(得分:14)
访问者模式可以用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
并返回,访问者对象知道要使用的数据类。
答案 1 :(得分:9)
您可以使用装饰器来获得您想要的东西。复制此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
修改强>
obj.__qualname__
在Python 3.3之前是不可用的,所以我们必须使用hack来获得更低版本: -
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以下的python版本不起作用,因为方法在传递给装饰器时仍然是常规函数。您可以尝试同时使用类和方法装饰器,请参阅Can a Python decorator of an instance method access the class?。
答案 2 :(得分: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'>
答案 3 :(得分:1)
如果有人发现它有用,我在Python 2中使用内省工作时得到了Joren's answer @visitor
的以下版本:
_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
答案 4 :(得分:0)
首先
ingredients: [{
id: 2,
amount: "1",
unit: "BUND"
}]
)的动态分配您可以在python中以相同的方式执行此操作,但是您也可以使用装饰器实现双重分发。 (Lisp的CLOS使用类似的方法)
this