Interpreting ASTs in Python 3.6: isinstance vs. monkey-patching vs. visit_NodeType vs. macros?
假设我想写一个小翻译可以用二进制运算计算表达式
我目前只对AST的解释感兴趣,为了简单起见,让我们跳过标记化技术和解析。
在haskell中,有一种或多或少规范的方法可以做到这一点:
1 2 3 4 5 6 7 8 | data Ast = Plus Ast Ast | Negate Ast | IntConst Int ev :: Ast -> Int ev (Plus a b) = (ev a) + (ev b) ev (Negate x) = - (ev x) ev (IntConst i) = i main = print $ show $ ev $ (Plus (IntConst 50) (Negate $ IntConst 8)) |
现在,python 3.6似乎没有代数数据类型。我的问题是似乎有很多可能的解决办法。最明显的一个正在使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Plus: def __init__(self, first, second): self.first = first self.second = second class Negate: def __init__(self, first): self.first = first class IntConst: def __init__(self, value): self.value = value def ev(ast): if isinstance(ast, Plus): return ev(ast.first) + ev(ast.second) elif isinstance(ast, Negate): return - ev(ast.first) elif isinstance(ast, IntConst): return ast.value print(ev(Plus(IntConst(50), Negate(IntConst(8))))) |
号
这可以按预期工作并打印
这里还有几个选项,但每个选项都有一些缺点:
我没有找到这个相关问题的答案令人满意,因为唯一的答案只是链接到一些外部工具,而且它不再被维护。
那么,在python 3.6.x中是否有一些标准的方法来定义AST的解释程序?没有上述缺点吗?还是我应该坚持使用
编辑
利用@juanpa.arrivilaga提出的
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from collections import namedtuple from functools import singledispatch Plus = namedtuple('Plus', ['left', 'right']) Negate = namedtuple('Negate', ['arg']) IntConst = namedtuple('IntConst', ['value']) @singledispatch def ev(x): raise NotImplementedError("not exhaustive: %r" % (type(x))) ev.register(Plus, lambda p: ev(p.left) + ev(p.right)) ev.register(Negate, lambda n: -ev(n.arg)) ev.register(IntConst, lambda c: c.value) print(ev(Plus(IntConst(50), Negate(IntConst(8))))) |
但是,如果
如果您正在寻找更干净的代码,我认为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import functools class Plus: def __init__(self, first, second): self.first = first self.second = second class Negate: def __init__(self, first): self.first = first class IntConst: def __init__(self, value): self.value = value @functools.singledispatch def ev(ast): raise NotImplementedError('Unsupported type') @ev.register(Plus) def _(ast): return ev(ast.first) + ev(ast.second) @ev.register(Negate) def _(ast): return -ev(ast.first) @ev.register(IntConst) def _(ast): return ast.value print(ev(Plus(IntConst(50), Negate(IntConst(8))))) |