python - 如何在不使用 eval() 的情况下将字符串的用户输入评估为数学表达式?
问题描述
我需要制作一个程序来接受数学表达式的用户输入(expr)并处理找到答案。但是,我们不允许使用 eval() 函数。赋值声明我们应该假设用户将输入的唯一运算符是:+、-、*、/、%。操作数也被假定为一位整数。
我的想法是将操作数转换为整数并列出所有可以使用的运算符。然后,使用 if 语句查看运算符如何匹配我的列表。
到目前为止,我能够想出这些代码行:
operand1 = expr[1]
operator = expr[2]
operand2 = expr[3]
operators = ['+','-','*','/','%']
我这样做是为了索引输入表达式中每个操作数和运算符的位置。我被困在这里,希望有人能给我一些关于如何前进的帮助。代码的结果需要输出用户输入的表达式,以及表达式的结果。如果表达式的第二个操作数是 0 并且运算符是除法,则代码输出“无”。
解决方案
您可以使用 解析表达式ast.parse
。
>>> import ast
>>> expr = ast.parse("3 + 5", mode="eval")
然后您可以分析生成的解析树。在这种特殊情况下,您关心表达式的主体。
>>> expr
<_ast.Expression object at 0x10992c438>
>>> expr.body
<_ast.BinOp object at 0x10a926320>
该对象具有感兴趣的属性:left
、op
和right
。你会看运营商
>>> expr.body.op
<_ast.Add object at 0x10a91a208>
决定如何处理操作数。
>>> expr.body.left.n + expr.body.right.n
8
所以一个可以处理乘法和加法的简单递归函数可能看起来像
def evaluate_expr(expr):
if isinstance(expr, ast.Expression):
return evaluate_expr(expr.body)
elif isinstance(expr, ast.Num):
return expr.n
elif isinstance(expr, ast.BinOp):
op = expr.op
left = evaluate_expr(expr.left)
right = evaluate_expr(expr.right)
if isinstance(op, ast.Add):
return left + right
elif isinstance(op, ast.Mult):
return left * right
raise ValueError(f"Can't evaluate {expr}")
e = ast.parse("3 + 5 * 2", mode="eval")
print(evaluate_expr(e.body)) # Outputs 13
请参阅ast
模块的文档以了解树中可以出现哪些其他节点,以便您可以适应evaluate_expr
处理其他操作,括号等ast.dump
也有助于探索表达式是如何解析的。
>>> ast.dump(e, annotate_fields=False)
'Expression(BinOp(Num(3), Add(), BinOp(Num(5), Mult(), Num(2))))'
这清楚地表明解析器处理优先级:3 + 5 * 2
不是3 + 5
将结果乘以 2,而是3
加上5 * 2
(较低的节点具有较高的优先级,因为树是自下而上评估的)的结果。
这假定您的输入实际上是一个有效的 Python 表达式。如果没有,您将需要编写自己的解析器,但是一旦有了解析树,该树的评估就会以类似的方式进行(尽管树的节点是您在解析中创建的任何内容,不一定是ast.Bin
et al . 由 ) 创建的节点ast.parse
。
推荐阅读
- calculation - 注册篮子计算结果视图
- c++ - 通过“\0”而不是 memset() 批量初始化 char 数组
- sql-server - 如何使用 where 子句将值传递给子查询的日期变量部分
- php - 在 wp-admin 列中显示每个 woocommerce 变化的库存数量
- nginx - nginx is replying with an error 403 after opening URL
- c++ - 忽略 C++ 中的变量类型
- node.js - Wait for one plugin to finish registering, before proceeding to register the next one
- php - Search for value in array and remove element before and after
- java - 使用连接多个视图的单个手势检测器获得双击/触摸视图
- excel - 为什么我的电子表格不能识别重复的日期/时间?