Python:如何使用ast重载运算符
我的问题是用ast重新定义计算表达式的Python:如何使用ast重载运算符,python,overloading,abstract-syntax-tree,Python,Overloading,Abstract Syntax Tree,我的问题是用ast重新定义计算表达式的+-运算符。 我有一个表达式列表,使用eval()解析很简单: 但是我喜欢像这样重新定义list和dict的+-操作符(addition): expr = '[1,2,3]+[4,5,6]' expr = "{'a':1, 'b':2} + {'a':3, 'b':4}" eval的常规结果是 [1,2,3,4,5,6] 但是我想要 [5,7,9] 就像在R语言中一样 这同样适用于以下词典: expr = '[1,2,3]+[4,5,6]' expr
+
-运算符。
我有一个表达式列表,使用eval()解析很简单:
但是我喜欢像这样重新定义list和dict的+
-操作符(addition):
expr = '[1,2,3]+[4,5,6]'
expr = "{'a':1, 'b':2} + {'a':3, 'b':4}"
eval的常规结果是
[1,2,3,4,5,6]
但是我想要
[5,7,9]
就像在R语言中一样
这同样适用于以下词典:
expr = '[1,2,3]+[4,5,6]'
expr = "{'a':1, 'b':2} + {'a':3, 'b':4}"
我想要一个
{'a':4,'b':6}
简而言之,我认为要取代普通的加法函数,即当操作数是list或dict时,正确的操作
我尝试使用
ast
和NodeTransformer
,但没有成功。有人可以帮我吗?创建您自己的列表类并在其上定义加法运算符:
class MyKindOfList(list):
def __add__(self, other):
return MyKindOfList(a + b for a, b in zip(self, other))
然后你可以这样做:
x = MyKindOfList([1, 2, 3])
y = MyKindOfList([4, 5, 6])
print (x + y) # prints [5, 7, 9]
即使使用
ast
模块,也不能重载内置类(如list
和dict
)的\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。但是,您可以将所有添加(如x+y
)重写为函数调用(如自定义添加(x,y)
)
这实质上是一个三步过程:
使用解析输入表达式
使用重写函数调用的所有添加内容
解析自定义加法函数的源代码,并将其添加到步骤1中获得的抽象语法树中
代码
警告
- 添加函数将以名称
\uu custom\u add
添加到全局范围-它与任何其他全局函数一样可访问,并且可能被覆盖、隐藏、删除或以其他方式篡改
从Aran Fey的建议开始,读了一些文章,我写了一个更可读的代码来解决这个问题
import ast
from itertools import zip_longest
def __custom_add(lhs, rhs):
if isinstance(lhs,list) and isinstance(rhs, list):
return [__custom_add(l, r) for l, r in zip_longest(lhs, rhs, fillvalue=0)]
if isinstance(lhs, dict) and isinstance(rhs, dict):
keys = lhs.keys() | rhs.keys()
return {key: __custom_add(lhs.get(key,0), rhs.get(key,0)) for key in keys}
return lhs + rhs
class SumTransformer(ast.NodeTransformer):
def visit_BinOp(self, node):
if isinstance(node.op, ast.Add):
new_node = ast.Call(func=ast.Name(id='__custom_add', ctx=ast.Load()),
args=[node.left, node.right],
keywords = [],
starargs = None, kwargs= None
)
ast.copy_location(new_node, node)
ast.fix_missing_locations(new_node)
return new_node
return node
expr = [
'(2 + 3 * 4)/2',
'[1, 2] + [3, 4]',
"{'a': 1} + {'a': -2}"
]
for e in expr:
syntax_tree = ast.parse(e, mode='eval')
syntax_tree = SumTransformer().visit(syntax_tree)
res = eval(compile(syntax_tree, '<ast>', 'eval'))
print(res)
# results
# 7.0
# [4, 6]
# {'a': -1}
导入ast
从itertools导入zip\u
定义自定义添加(左侧、右侧):
如果isinstance(左侧,列表)和isinstance(右侧,列表):
返回[\u自定义\u在zip中为l,r添加(l,r)\u最长(lhs,rhs,fillvalue=0)]
如果isinstance(左侧,dict)和isinstance(右侧,dict):
keys=lhs.keys()| rhs.keys()
返回{key:uuu custom_uadd(lhs.get(key,0),rhs.get(key,0))for key in key}
返回左侧+右侧
等级SumTransformer(ast.NodeTransformer):
def visit_BinOp(自身,节点):
如果isinstance(node.op,ast.Add):
new_node=ast.Call(func=ast.Name(id=''自定义添加',ctx=ast.Load()),
args=[node.left,node.right],
关键词=[],
starargs=None,kwargs=None
)
复制位置(新节点,节点)
修复缺少的位置(新节点)
返回新节点
返回节点
表达式=[
'(2 + 3 * 4)/2',
'[1, 2] + [3, 4]',
“{'a':1}+{'a':-2}”
]
对于expr中的e:
语法树=ast.parse(e,mode='eval')
语法树=SumTransformer()。访问(语法树)
res=eval(编译(语法树,,'eval'))
打印(res)
#结果
# 7.0
# [4, 6]
#{'a':-1}
感谢所有帮助我的人我可以问一下为什么吗?我不认为拥有自己的Python变体与标准Python不可互操作是一个好主意。我认为您应该将列表
和dict
子类化,并覆盖现有的\uu添加
。这不完全是我想要的。问题是:我有一个包含很多表达式的文件,我可以用eval()函数解决这些表达式。Aran Frey的答案是正确的谢谢,这是我正在寻找的解决方案。我将尝试将源代码修改为使用compile和“eval”模式,因为我将只使用表达式。