Python 高手编程系列九百一十一:访问者

访问者(visitor)帮助分离算法与数据结构,并具有与观察者模式类似的目标。它允许扩
展给定类的功能而不改变其代码。但是访问者做的更多的是,通过定义一个负责保存数据的类,
并将算法推送到称为访问者的其他类。每个访问者专用于一种算法,并且可以将其应用于数据。
这种行为与 MVC 范式(参考 http://en.wikipedia.org/wiki/Model-view-controller)非常
相似,其中文档是通过控制器推送到视图的被动容器,或者模型包含被控制器改变的数据。
访问者模式通过在数据类中提供可由各种访问者访问的入口点来实现。通用描述是一
个 Visitable 类,它接受 Visitor 实例并调用它们
Visitable 类决定它如何调用 Visitor 类,例如,通过决定调用哪个方法。例如,
负责打印内置类型内容的访问者可以实现 visit_TYPENAME()方法,并且每个类型都可
以在 accept()方法中调用给定的方法,如下所示:
class VisitableList(list):
def accept(self, visitor):
visitor.visit_list(self)
class VisitableDict(dict):
def accept(self, visitor):
visitor.visit_dict(self)
class Printer(object):
def visit_list(self, instance):
print(‘list content: {}’.format(instance))
def visit_dict(self, instance):
print(‘dict keys: {}’.format(
', '.join(instance.keys()))
)
下面的示例就是这样做的:

visitable_list = VisitableList([1, 2, 5])
visitable_list.accept(Printer())
list content: [1, 2, 5]
visitable_dict = VisitableDict({‘one’: 1, ‘two’: 2, ‘three’: 3})
visitable_dict.accept(Printer())
dict keys: two, one, three
但是这种模式意味着每个被访问的类需要有一个被访问的 accept 方法,这是非常痛
苦的。
由于 Python 允许代码内省,一个更好的主意是自动关联访问者和被访问的类,如
下所示:
def visit(visited, visitor):
… cls = visited.class.name
… method_name = ‘visit_%s’ % cls
… method = getattr(visitor, method_name, None)
… if isinstance(method, Callable):
… method(visited)
… else:
… raise AttributeError(
… “No suitable ‘{}’ method in visitor”
… “”.format(method_name)
… )

visit([1,2,3], Printer())
list content: [1, 2, 3]
visit({‘one’: 1, ‘two’: 2, ‘three’: 3}, Printer())
dict keys: two, one, three
visit((1, 2, 3), Printer())
Traceback (most recent call last):
File “”, line 1, in
File “”, line 10, in visit
AttributeError: No suitable ‘visit_tuple’ method in visitor
该模式以这种方式在 ast 模块中使用,例如,通过调用访问者的 NodeVisitor 类与
编译代码树的每个节点。这是因为 Python 没有像 Haskell 这样的匹配运算符。
另一个例子是根据文件扩展名调用 Visitor 方法的目录遍历器,如下所示:
def visit(directory, visitor):
… for root, dirs, files in os.walk(directory):
… for file in files:
… # foo.txt → .txt
… ext = os.path.splitext(file)[-1][1:]
… if hasattr(visitor, ext):
… getattr(visitor, ext)(file)

class FileReader(object):
… def pdf(self, filename):
… print(‘processing: {}’.format(filename))

walker = visit(‘/Users/tarek/Desktop’, FileReader())
processing slides.pdf
processing sholl23.pdf
如果你的应用程序具有由多个算法访问的数据结构,则访问者模式将有助于分离关
注点。对于数据容器来说,最好只专注于提供数据访问和持有数据,而无需关心其他任
何事情。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值