此处不确定术语,但这可能是方案中eq?
和equal?
之间的差异,也可能是带有C字符串的==
和strncmp
之间的差异;在每种情况下,第一个将返回false为两个不同的字符串,实际上具有相同的内容,第二个将返回true。
我正在寻找后者的操作,用于Python的AST。
现在,我正在这样做:
import ast
def AST_eq(a, b):
return ast.dump(a) == ast.dump(b)
显然有效,但感觉像是等待发生的灾难。有人知道更好的方法吗?
修改:遗憾的是,当我比较两个AST'__dict__
时,该比较默认使用单个元素'__eq__
方法。 AST被实现为其他AST的树,并且它们的__eq__
显然检查参考标识。所以既不直接==
也不是托马斯链接中的解决方案。 (除此之外,我也不想将每个AST节点类型子类化以插入此自定义__eq__
。)
答案 0 :(得分:4)
我遇到了同样的问题。我试着这样做:首先将AST愚蠢到一些更容易的表示(一个dicts树):
def simplify(node):
if isinstance(node, ast.AST):
res = vars(node).copy()
for k in 'lineno', 'col_offset', 'ctx':
res.pop(k, None)
for k, v in res.iteritems():
res[k] = simplify(v)
res['__type__'] = type(node).__name__
return res
elif isinstance(node, list):
return map(simplify, node)
else:
return node
然后你可以比较这些表示:
data = open("/usr/lib/python2.7/ast.py").read()
a1 = ast.parse(data)
a2 = ast.parse(data)
print simplify(a1) == simplify(a2)
会给你True
修改强>
只是明白没有必要创建一个字典,所以你可以这样做:
def compare_ast(node1, node2):
if type(node1) is not type(node2):
return False
if isinstance(node1, ast.AST):
for k, v in vars(node1).iteritems():
if k in ('lineno', 'col_offset', 'ctx'):
continue
if not compare_ast(v, getattr(node2, k)):
return False
return True
elif isinstance(node1, list):
return all(itertools.starmap(compare_ast, itertools.izip(node1, node2)))
else:
return node1 == node2
答案 1 :(得分:3)
以下适用于Python 2或3,并且比使用itertools更快:
编辑:警告:
显然,这段代码可能会在某些(奇怪的)情况下出现。因此,我不能推荐它。
def compare_ast(node1, node2):
if type(node1) != type(node2):
return False
elif isinstance(node1, ast.AST):
for kind, var in vars(node1).items():
if kind not in ('lineno', 'col_offset', 'ctx'):
var2 = vars(node2).get(kind)
if not compare_ast(var, var2):
return False
return True
elif isinstance(node1, list):
if len(node1) != len(node2):
return False
for i in range(len(node1)):
if not compare_ast(node1[i], node2[i]):
return False
return True
else:
return node1 == node2
答案 2 :(得分:1)
在Python中,使用is
运算符(与==
不同,不能重载)来比较对象标识。除非由白痴实现,==
不会比较身份,而是相等(当然,如果可能并实施)。在内置字符串类的情况下,情况肯定不是这样。
但是,您的实现可能存在另一个问题 - 因为转储会生成非常精确的信息(适合于调试),例如,名为different的变量可以被视为!=
。这可能是也可能不是你想要的。
答案 3 :(得分:1)
我修改了@Yorik.sar 对 Python 3.9+ 的回答:
def compare_ast(node1: Union[ast.expr, List[ast.expr]], node2: Union[ast.expr, List[ast.expr]]) -> bool:
if type(node1) is not type(node2):
return False
if isinstance(node1, ast.AST):
for k, v in vars(node1).items():
if k in ("lineno", "col_offset", "ctx"):
continue
if not compare_ast(v, getattr(node2, k)):
return False
return True
elif isinstance(node1, list) and isinstance(node2, list):
return all([compare_ast(n1, n2) for n1, n2 in zip(node1, node2)])
else:
return node1 == node2