这个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)
- 我不知道
visitor
的数据类型,但我认为它是一个字符串 visitor
被分配给getattr
,尽管我不知道它试图从哪个对象获取属性- 我不知道
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
,所以在不知道它的数据类型的情况下,我真的无法继续使用解释器。
关于您关于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
的一个子类。
node
是AST
子类的一个实例。visitor
是NodeVisitor
类的子类(您应该自己编写(上的一个方法。它是动态查找的,因为传入的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_Expression
或visit_Call
等方法,这些节点被传递给NodeWalker.generic_visit()
方法,后者处理每个字段并为任何其他节点调用self.visit()
。