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

本文关键字:node 抽象 语法树 是什么 Python 变量 数据类型 这个 | 更新日期: 2023-09-27 18:15:43

我正在使用Ruslan Pavik的指南用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的数据类型是什么

稍后在指南中,他宣布了这个类:

class AST(object):
pass

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

AST之后的下一个类是继承AST:的BinOp

class BinOp(AST):
def __init__(self, left, op, right):
    self.left = left
    self.token = self.op = op
    self.right = right

基本上我现在真的很困惑。但我最困惑的是node,因为我不知道它有什么数据类型。由于解析器和解释器中的许多类都使用node,所以在不知道它的数据类型的情况下,我真的无法继续使用解释器。

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

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

因此,在您的案例getattr(self, method_name, self.generic_visit)中,它试图从自身获取一个名为method_name的属性。如果它不存在,则使用self.generic_visit。现在,如果method_name"visit_BinOp",它将尝试获得self.visit_BinOp,如果不存在,它将获得self.generic_visit

最后,如果不运行代码,就无法确定它将返回什么或visitor的类型。理论上它可以是任何东西,但通常它是一个方法。但这与Python和C#不同,Python中变量的类型取决于分配给它的内容

i = 42
# i is now an int, but you can easily overwrite it with a string
i = "Hello World"

node也是如此,理论上它可以是任何东西,但如果使用正确,它将是AST的一个子类。

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

AST是节点的基类。CCD_ 31是一个特定的节点类;不同的类记录在抽象语法部分;每个camel大小写的名称也是一个CCD_ 32子类。

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

为了更具体一点:假设您对特定的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()