这个Python变量的数据类型是什么:node(抽象语法树)

时间:2016-10-31 13:06:51

标签: c# python parsing interpreter

我正在使用Ruslan Pavik's Guide在C#中构建自己的解释器。我在第7部分,我们正在创建一个抽象语法树。我能够通过第一部分获得并能够将指南中的Python代码翻译成C#。但是在第7部分中,对于没有Python经验的人来说变得非常困难。

我很难弄清楚数据类型和返回类型是什么,Python是一种动态编程语言。

我对这部分感到困惑:

 def visit(self, node):
    method_name = 'visit_' + type(node).__name__
    visitor = getattr(self, method_name, self.generic_visit)
    return visitor(node)
  1. 我不知道visitor的数据类型,但我认为它是一个字符串。
  2. visitor被分配getattr,但我不知道它试图从哪个对象获取属性。
  3. 我不知道node的数据类型是什么。
  4. 后来在指南中,他宣布了这堂课:

    class AST(object):
    pass
    

    但是我知道在C#中它只是一个在大括号内没有任何内容的类:public class AST { }

    AST之后的下一课是继承BinOp的{​​{1}}:

    AST

    基本上我现在真的很困惑。但我的主要困惑是class BinOp(AST): def __init__(self, left, op, right): self.left = left self.token = self.op = op self.right = right ,因为我不知道它有什么数据类型。由于解析器和解释器中的很多类都使用node,因此在不知道它具有什么数据类型的情况下,我真的不能继续使用我的解释器。

2 个答案:

答案 0 :(得分:0)

nodeAST的子类的实例。 visitorNodeVisitor类的子类(您应该自己编写)的方法。它被动态查找 ,因为传入的node将是AST的任何一个可能的子类,并且您应该实现特定的visit_自定义子类上特定节点的方法。后备是使用self.generic_visit方法。

AST是节点的基类。 BinOp是特定的节点类;不同的班级是documented in the Abstract Grammar section;每个camel-cased名称也是AST子类。

通过特定节点的名称动态查找方法,开发人员避免了为语法定义的每个节点类型创建特定的具体方法。

为了使这更具体一点:说你对特定的BinOp运算符节点感兴趣;也许是因为你想分析一段代码中+的使用方式。

然后,您可以实现NodeVisitor子类,并在该子类上添加visit_BinOp()方法,并在传入节点的时自动调用它到NodeVisitorSubclass().visit(toplevel_node);对于没有特定visit_*方法的任何节点,NodeVisitor.generic_visit()方法将确保访问树的子节点(通过iter_fields()函数)。

演示:

>>> import ast
>>> class DemoVisitor(ast.NodeVisitor):
...     def visit_BinOp(self, binop_node):
...         if isinstance(binop_node.op, ast.Add):
...             print('Found addition of {} and {}'.format(
...                 ast.dump(binop_node.left), ast.dump(binop_node.right)))
...
>>> tree = ast.parse('function(foo + bar)', '', 'eval')
>>> ast.dump(tree)
"Expression(body=Call(func=Name(id='function', ctx=Load()), args=[BinOp(left=Name(id='foo', ctx=Load()), op=Add(), right=Name(id='bar', ctx=Load()))], keywords=[]))"
>>> DemoVisitor().visit(tree)
Found addition of Name(id='foo', ctx=Load()) and Name(id='bar', ctx=Load())

在上面的演示中,NodeWalker.visit()方法找到了visit_BinOp方法并为我们调用了该方法,但由于没有visit_Expressionvisit_Call等方法,这些节点被传递给NodeWalker.generic_visit()方法,它处理每个字段并为任何其他节点调用self.visit()

答案 1 :(得分:0)

关于getattr的问题:这只是一个函数,它返回一个具有给定名称(在第二个参数中)的对象(在第一个参数中)和一个后退(在第三个参数中)的属性参数)如果没有属性。

因此,在您的情况method_name中,它会尝试从自身获取名称为self.generic_visit的属性。如果它不存在则使用method_name。现在,如果"visit_BinOp"self.visit_BinOp,则会尝试获取self.generic_visit,如果不存在,则转而获取visitor

最后,在不运行代码的情况下,无法确定它将返回什么或i = 42 # i is now an int, but you can easily overwrite it with a string i = "Hello World" 将是什么类型。它可以是理论上的任何东西,但通常它是一种方法。但这与Python和C#不同,Python中变量的类型取决于分配给它的内容。您可以毫无问题地执行以下操作:

node

同样适用于AST,理论上它可以是任何东西,但如果使用正确,它将成为foo = [1, 1, 1, 5, 5, 5, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8] print foo[::3] 的子类。