Python实现访问者模式,更加高效、通用的解析方法

    当我们需要编写代码来处理或者遍历一个由许多不同类型的对象组成的复杂数据结构,每种类型的对象处理的方式都不相同。例如遍历一个树结构,根据遇到的树节点的类型来执行不同的操作。

    为了展示不同的类的处理,首先定义一些需要用到的类:

class Node:                       
    pass
 
class UnaryOperator(Node):          
    def __inif__(self, operand):
        self.operand = operand
 
class BinaryOperator(Node):
    def __init__(self, left, right):
        self.left = left
        self.right = right
 
class Number(Node):
    def __init__(self, value):
        self.value = value
 
class Add(BinaryOperator):
    pass
 
class Sub(BinaryOperator):
    pass
 
class Mul(BinaryOperator):
    pass
 
class Div(BinaryOperator):
    pass
 
class Negate(UnaryOperator):
    pass

    之后我们可以如下使用这些类:

>>> #表示 1 + 2 * (3 - 4) / 5
>>> t1 = Sub(Number(3), Number(4))
>>> t2 = Mul(Number(2), t1)
>>> t3 = Div(t2, Number(5))
>>> t4 = Add(Number(1), t3)

    但是问题并不在于如何创建这些数据结构上,而在编写处理它们的代码上。在访问中,想根据不同的数据类型来调度不同的处理办法,一种幼稚的办法是编写大量的if语句,但是这样的方法往往是不可行的。除了非常繁琐,运行速度慢之外,如果想要添加或者修改要处理的节点类型则会难以维护。

    为此,我们可以选择构造访问者类,实现“访问者模式”,使用一些小技巧将方法名构建出来,再利用getattr()函数来获取方法会好得多。

    如下构造节点访问者类:

class NodeVistor:
    def vist(self, node):
        methodname = 'vist_' + type(node).__name__
        method = getattr(self, methodname, None)#未找到方法则返回None
        if method is None:
            raise RuntimeError('No {} method'.format('visit_'+type(node).__name__))
        return method(node)

    这个节点访问者类作为基类,它提供了访问者类的核心方法vist()的定义。并且使用methodname获取应该要调用的子类的方法,使用getattr获取应该调用的方法。如果该方法存在,则使用method进行调用,如果不存在,则抛出错误。

调用对象的方法,方法名以字符串的方法给出,戳这里

    如下构建访问者类:

class Vistor(NodeVistor):
    def vist_Number(self, node):
        return node.value
 
    def vist_Add(self, node):
        return self.vist(node.left) + self.vist(node.right)
 
    def vist_Sub(self, node):
        return self.vist(node.left) - self.vist(node.right)
 
    def vist_Mul(self, node):
        return self.vist(node.left) * self.vist(node.right)
 
    def vist_Div(self, node):
        return self.vist(node.left) / self.vist(node.right)
 
    def vist_Negate(self, node):
        return -node.operand

    这个访问者类使用了对vist()方法进行递归的方式来完成计算。正是因为递归才使得访问者类可以遍历整个数据结构,最终直到比如Number类结束。

    以下是访问者类的使用示例:

>>> v = Vistor()
>>> v.vist(t4)
0.6
>>> v.vist(t3)
-0.4
>>> v.vist(t2)
-2
>>> v.vist(t1)
-1

    访问者模式的一个缺点就是需要重度依赖于递归。如果要处理一个深度嵌套的数据结构,那么有可能会达到Python的递归深度限制(可以使用sys.getrecursionlimit()查看)。要避免这个问题,可以在构建数据结构时做一些特定的选择。比如使用普通的python列表来替代链表,或者在每一个节点中聚合更多的数据,使得数据更加扁平化而不是深度嵌套。

更多技术干货,点这里就够了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
访问者模式是一种行为设计模式,它允许你定义一系列操作,而这些操作可以应用于某个对象结构中的元素。通过这种方式,你可以在不改变各元素类的前提下,定义作用于这些元素的新操作。 在 Python 中,访问者模式通常由两个类组成:元素类和访问者类。元素类表示对象结构中的元素,而访问者类则表示要对这些元素执行的操作。 下面是一个简单的示例代码: ```python class Element: def accept(self, visitor): pass class ConcreteElementA(Element): def accept(self, visitor): visitor.visit_concrete_element_a(self) class ConcreteElementB(Element): def accept(self, visitor): visitor.visit_concrete_element_b(self) class Visitor: def visit_concrete_element_a(self, element): pass def visit_concrete_element_b(self, element): pass class ConcreteVisitor1(Visitor): def visit_concrete_element_a(self, element): print("ConcreteVisitor1 visited ConcreteElementA") def visit_concrete_element_b(self, element): print("ConcreteVisitor1 visited ConcreteElementB") class ConcreteVisitor2(Visitor): def visit_concrete_element_a(self, element): print("ConcreteVisitor2 visited ConcreteElementA") def visit_concrete_element_b(self, element): print("ConcreteVisitor2 visited ConcreteElementB") elements = [ConcreteElementA(), ConcreteElementB()] visitors = [ConcreteVisitor1(), ConcreteVisitor2()] for element in elements: for visitor in visitors: element.accept(visitor) ``` 在上面的代码中,我们定义了两个元素类 ConcreteElementA 和 ConcreteElementB,以及两个访问者类 ConcreteVisitor1 和 ConcreteVisitor2。元素类都实现了 accept 方法,该方法接受一个访问者对象作为参数,并调用该访问者对象的 visit 方法。访问者类则实现了 visit 方法,该方法接受一个元素对象作为参数,并对该元素对象执行相应的操作。 在主程序中,我们创建了两个元素对象和两个访问者对象,并依次将每个元素对象传递给每个访问者对象的 visit 方法。这样,每个访问者对象都会对每个元素对象执行相应的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值