如何使用 Python 从 JSON 返回算术表达式的结果?

新人学 Python,我想写一个 Python 程序,该程序可以采用 JSON 格式的嵌套表达式,解析和评估答案.

比如這一個 json 文件 { "root": { "description": "This is your first nested expression, and it evaluates to 8. If you have difficulties understanding it, it reads (2*3)+(4-2).", "plus": [{ "times": [{ "int": 2 }, { "int": 3 }] }, { "minus": [{ "int": 4 }, { "int": 2 }] }] } }

像 PLUS 这样的每个运算符都至少使用 2 个参数,MINS 和 TIMES 恰好采用 2 个参数.

我尝试了这样来写

def operation(d):

if isinstance(d,dict):
    s = 0
    addition = d.get('root').get('plus')
for i in addition:
    s+=int(i["int"])
return s

result = operation(z)

当我发现了这样只是返回了 int 的值而已, 并没有做任何的递归. 请问要怎么写才可以呢??


如何使用 Python 从 JSON 返回算术表达式的结果?

9 回复

你去搜搜波兰表达式怎么算。或者看看算法的书。应该是转成个二叉树吧。


import json
import operator

def evaluate_expression(data):
    """
    从JSON数据中解析并计算算术表达式
    
    Args:
        data: 包含表达式的JSON字符串或字典
        
    Returns:
        计算结果
    """
    # 如果是字符串,先解析为字典
    if isinstance(data, str):
        data = json.loads(data)
    
    # 支持的运算符映射
    operators = {
        '+': operator.add,
        '-': operator.sub,
        '*': operator.mul,
        '/': operator.truediv,
        '**': operator.pow,
        '%': operator.mod
    }
    
    def _evaluate(node):
        """递归计算表达式树"""
        if isinstance(node, (int, float)):
            return node
        
        if isinstance(node, list) and len(node) == 3:
            op, left, right = node
            if op in operators:
                return operators[op](_evaluate(left), _evaluate(right))
            else:
                raise ValueError(f"不支持的运算符: {op}")
        
        raise ValueError(f"无效的表达式节点: {node}")
    
    return _evaluate(data)

# 使用示例
if __name__ == "__main__":
    # 示例1: 直接使用字典
    expr1 = ["+", 5, ["*", 3, 2]]  # 5 + (3 * 2)
    print(f"5 + (3 * 2) = {evaluate_expression(expr1)}")  # 输出: 11
    
    # 示例2: 使用JSON字符串
    expr2_json = '["-", ["**", 2, 3], ["/", 10, 2]]'  # 2^3 - (10/2)
    print(f"2^3 - (10/2) = {evaluate_expression(expr2_json)}")  # 输出: 3.0
    
    # 示例3: 复杂表达式
    expr3 = ["+", ["*", 2, 3], ["-", 10, ["/", 8, 2]]]  # (2*3) + (10 - (8/2))
    print(f"(2*3) + (10 - (8/2)) = {evaluate_expression(expr3)}")  # 输出: 12.0

这个方案用前缀表达式(波兰表示法)来存储表达式,每个运算都是 [运算符, 左操作数, 右操作数] 的三元列表。evaluate_expression 函数递归解析这个结构,用 operator 模块执行实际计算。

表达式可以写成字典直接传进去,或者用JSON字符串也行。代码里 operators 字典定义了支持的运算符,要加别的运算符(比如 // 整除)直接往里面加映射就行。

注意除法和乘方运算返回的是浮点数,整数除法得用 // 运算符。

用递归遍历表达式树,遇到数字就返回,遇到运算就递归算左右两边再计算。

或者后缀记法,用栈。

勇气可嘉,参考:wangyin 《 DSL 的误区 》
python 格式的运算式语法多简单易懂啊,直接写运算式,过滤一下然后 eval 不好么~

啥叫递归?递归至少要调用自己阿。。。

請問怎麼過濾?

本来我的意思是直接

expr_str = “”"(23)+(4-2)"""
ans = eval(expr_str)

后来发现你好像是在做 python 题学习递归,假期要结束了,刚好蛋疼给你写了个答案:

# -
- coding: utf-8 --
import json

def is_raw_type(d : dict) -> bool:
type_words = [‘int’]
for k in d.keys():
if k not in type_words:
return False
return True

def is_basic_expr(d : dict) -> bool:
op_words = [‘plus’, ‘times’, ‘minus’]
if len(d.keys()) != 1:
return False
else:
k,v = d.copy().popitem()
if k in op_words:
if isinstance(v,dict):
return is_raw_type(v)
if isinstance(v,list):
return (sum([not is_raw_type(kv) for kv in v]) == 0)

def calc_parser(d : dict):
if is_raw_type(d):
return d
elif is_basic_expr(d):
k,v = d.popitem()
if k == “plus”:
return {‘int’:sum([int(sub_v[‘int’]) for sub_v in v])}
elif k == “minus”:
return {‘int’:v[0][‘int’]-v[1][‘int’]}
elif k == “times”:
s = 1
for sub_v in v:
s = int(s
sub_v[‘int’])
return {‘int’:s}
else:
return {‘int’:0}
elif len(d.keys()) == 1:
e = d.copy()
k,v = d.popitem()
e[k] = [calc_parser(sub_v) for sub_v in v]
return calc_parser(e)

test = “”"{ “root”: { “description”: “This is your first nested expression, and it evaluates to 8. If you have difficulties understanding it, it reads (2*3)+(4-2).”, “plus”: [{ “times”: [{ “int”: 2 }, { “int”: 3 }] }, { “minus”: [{ “int”: 4 }, { “int”: 2 }] }] } }"""
d_with_description = json.loads(test)
for k,v in d_with_description[‘root’].items():
if k != ‘description’:
d = {k:v}
print(calc_parser(d))

这也是为了证明题中自己发明的那种自定义 json 语法格式多蛋疼~

我操,缩进没了~我又上不去 gist,好绝望

aHR0cHM6Ly9naXRlZS5jb20vaWNlX3dhdmUvY29kZXMvYXk2ajVyM2lidDR6dTJmeHEwazh3NTU=

base64 解码一下就能看到地址

回到顶部