Python:如何使用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

我的问题是用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 = "{'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”模式,因为我将只使用表达式。