How to write the Visitor Pattern for Abstract Syntax Tree in Python?
我的同事建议我写一个访客模式来导航AST。有人能告诉我更多我该怎么开始写吗?
据我所知,ast中的每个节点都有
为了简化一切,假设我有节点
1 2 3 4 5 6 7 8 9 10 11 | Root | Op(+) / \ / \ Number(5) \ Op(*) / \ / \ / \ Number(2) Number(444) |
有人能想到访问者模式将如何访问此树以生成输出:
1 | 5 + 2 * 444 |
号
谢谢,博达·赛多。
维基百科对访问者模式的工作有很大的了解,尽管他们使用的示例实现是在Java中。不过,您可以很容易地将其移植到Python,不是吗?
基本上,您希望实现一个双调度机制。AST中的每个节点都需要实现一个
参见
1 2 3 4 5 6 7 8 9 | import ast class MyVisitor(ast.NodeVisitor): def visit_BinaryOp(self, node): self.visit(node.left) print node.op, self.visit(node.right) def visit_Num(self, node): print node.n, |
当然,即使在需要的地方,这也不会发出括号,所以实际上还有更多的工作要做,但是,这是一个开始;-)。
我在互联网上最常遇到的两种在python中实现访问者模式的变体:
- gamma等人从desigh patterns书中一对一地翻译示例。
- 使用附加模块进行双调度
从设计模式书翻译的例子
这种变体在数据结构类中使用
数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Operation(object): def __init__(self, op, arg1, arg2): self.op = op self.arg1 = arg1 self.arg2 = arg2 def accept(self, visitor): visitor.visitOperation(self) class Integer(object): def __init__(self, num): self.num = num def accept(self, visitor): visitor.visitInteger(self) class Float(object): def __init__(self, num): self.num = num def accept(self, visitor): visitor.visitFloat(self) expression = Operation('+', Integer('5'), Operation('*', Integer('2'), Float('444.1'))) |
号
中缀打印访问者
1 2 3 4 5 6 7 8 9 10 11 | class InfixPrintVisitor(object): def __init__(self): self.expression_string = '' def visitOperation(self, operation): operation.arg1.accept(self) self.expression_string += ' ' + operation.op + ' ' operation.arg2.accept(self) def visitInteger(self, number): self.expression_string += number.num def visitFloat(self, number): self.expression_string += number.num |
前缀打印访问者
1 2 3 4 5 6 7 8 9 10 11 12 | class PrefixPrintVisitor(object): def __init__(self): self.expression_string = '' def visitOperation(self, operation): self.expression_string += operation.op + ' ' operation.arg1.accept(self) self.expression_string += ' ' operation.arg2.accept(self) def visitInteger(self, number): self.expression_string += number.num def visitFloat(self, number): self.expression_string += number.num |
。
试验
1 2 3 4 5 6 | infixPrintVisitor = InfixPrintVisitor() expression.accept(infixPrintVisitor) print(infixPrintVisitor.expression_string) prefixPrintVisitor = PrefixPrintVisitor() expression.accept(prefixPrintVisitor) print(prefixPrintVisitor.expression_string) |
产量
1 2 | 5 + 2 * 444.1 + 5 * 2 444.1 |
。使用其他模块
此变体使用
数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Operation(object): def __init__(self, op, arg1, arg2): self.op = op self.arg1 = arg1 self.arg2 = arg2 class Integer(object): def __init__(self, num): self.num = num class Float(object): def __init__(self, num): self.num = num expression = Operation('+', Integer('5'), Operation('*', Integer('2'), Float('444.1'))) |
。
中缀打印访问者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from functools import singledispatch @singledispatch def visitor_print_infix(obj): pass @visitor_print_infix.register(Operation) def __(operation): return visitor_print_infix(operation.arg1) + ' ' \ + operation.op + ' ' \ + visitor_print_infix(operation.arg2) @visitor_print_infix.register(Integer) @visitor_print_infix.register(Float) def __(number): return number.num |
前缀打印访问者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from functools import singledispatch @singledispatch def visitor_print_prefix(obj): pass @visitor_print_prefix.register(Operation) def __(operation): return operation.op + ' ' \ + visitor_print_prefix(operation.arg1) + ' ' \ + visitor_print_prefix(operation.arg2) @visitor_print_prefix.register(Integer) @visitor_print_prefix.register(Float) def __(number): return number.num |
。
试验
1 2 | print(visitor_print_infix(expression)) print(visitor_print_prefix(expression)) |
产量
1 2 | 5 + 2 * 444.1 + 5 * 2 444.1 |
。
我之所以喜欢这种变体,是因为它消除了
对于3.4版之前的python,可以使用类似于singledispatch修饰符的多方法模块。多方法模块的一个缺点是,应用于给定数据结构元素的Visitor方法不仅基于元素的类型,而且基于方法的声明顺序进行选择。对于具有复杂继承层次结构的数据结构,将方法定义保持在正确的顺序可能很麻烦,而且容易出错。