相同的抽象语法树是否保证相同的行为?

问题描述 投票:1回答:1

给出两个生成相同抽象语法树(AST)的程序,是否保证它们在给定相同输入的情况下以相同的行为运行?

作为一个具体示例,我想对Python模块运行格式化程序以更改样式。为了检查格式化程序是否没有修改程序的逻辑,我想将格式化模块的AST与原始程序进行比较。这是一个好方法吗?

python formatting programming-languages abstract-syntax-tree
1个回答
0
投票

这个问题的非学问答案是肯定的,因为AST是编译器使用的中间表示形式; AST生成后,即用于生成字节码。一种简单的检查两个AST是否相同的方法是使用ast.dump函数,然后将结果作为字符串进行比较。


时髦的答案是,这取决于您所说的“相同”是什么意思-具体来说,取决于您要比较以确定它们是否相同的两个AST的属性。

例如,ast.dumpx = 1; raise ValueError()编译为“相同的” AST:

x = 1\nraise ValueError()

但是,AST还包括有关行号和位置的元数据,因此这两个AST并不完全相同:

>>> import ast
>>> print(ast.dump(ast.parse('x = 1; raise ValueError()')))
Module(body=[
  Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=1)),
  Raise(exc=Call(func=Name(id='ValueError', ctx=Load()), args=[], keywords=[]), cause=None)
])
>>> print(ast.dump(ast.parse('x = 1\nraise ValueError()')))
Module(body=[
  Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=1)),
  Raise(exc=Call(func=Name(id='ValueError', ctx=Load()), args=[], keywords=[]), cause=None)
])

此外,这些行号可在运行时在错误消息中使用;第一个说>>> ast.parse('x = 1; raise ValueError()').body[1].lineno 1 >>> ast.parse('x = 1\nraise ValueError()').body[1].lineno 2 ,第二个说line 1

line 2

从技术上讲,代码还可以检查错误消息中的行号,然后根据该行号决定其行为。 任何这样的代码都是可憎的,应该撤回并开枪射击,但是作为认证的学步车,我有责任注意这样的代码可以存在。

因此,从技术上讲,您的代码格式化程序不会导致真正“完全相同”的AST,因为它们的行/位置元数据可能会有所不同-您的代码格式化程序必须更改该元数据才能执行任何有用的操作。但这对于像您这样的自动代码格式设置工具来说是一个合理的警告,因为编写代码的人在重新设置格式时会中断,他们应该知道他们的代码太脆弱而无法被自动工具重新格式化。


为了完整性,如果要确保编译的字节码相同,则可以使用>>> exec('x = 1; raise ValueError()') Traceback (most recent call last): File "<pyshell#13>", line 1, in <module> exec('x = 1; raise ValueError()') File "<string>", line 1, in <module> ValueError >>> exec('x = 1\nraise ValueError()') Traceback (most recent call last): File "<pyshell#14>", line 1, in <module> exec('x = 1\nraise ValueError()') File "<string>", line 2, in <module> ValueError 函数:这比dis.get_instructions更为严格,因为字节码包含行号(但不包含行内的位置) ),但是如果格式化程序不应该在不同行之间移动代码,那么您可能更喜欢这种方式。

dis.get_instructions
© www.soinside.com 2019 - 2024. All rights reserved.