Python计算器(模拟eval)
先说说这个项目的目的:就想实现一个类似 Python 内置 eval 函数的功能,能处理带括号、加减乘除的复杂算术表达式,给你一个简单的计算器。当然,不谈安全性和性能,纯粹是练手理解递归和数据处理。
思路其实挺清晰的,核心就是三步走:
1. 把原始字符串拆成列表,方便逐项处理——用正则把数字、运算符、括号都分开。
2. 递归去掉最内层的括号,一层层往外剥,直到整个式子没有括号。这一步要处理好符号粘连问题,比如 - - 或 - 这类情况。
3. 没有括号后,先处理乘除,再处理加减,得到最终结果。
关键点在于括号的剥离方法。不能直接用 index() 去找左括号——因为 index 永远返回第一个匹配项,而非当前遇到的。所以得自己维护一个计数器 count,遍历列表时遇到左括号就记下索引,直到遇到第一个右括号,然后切片取出中间那部分,计算后替换掉原列表中的这一段。每替换一次就触发递归,直到括号全部消失。
替换后很可能出现 - 或 - - 这类多余符号,需要专门写一个 change() 函数来处理:检测到连续两个减号就合并成一个加号(或者正负抵消),类似的还有空格加减号等情况。这个函数在递归中和乘除运算后都需要调用。
乘除运算也是递归思路:从左到右遍历列表,遇到 * 或 / 就计算相邻两个数,然后替换结果、删除操作符和右操作数。然后递归调用自身,直到没有乘除符号。注意如果操作数是负数(即 * - 形式),要特殊处理符号。
加减运算相对简单,遍历列表累加即可,但也要注意第一个元素可能是负号的情况。最后根据结果的正负返回不同格式的列表(正数直接字符串,负数用 ['-', str(-sum)])。
整个流程通过 calculate() 函数判断是否包含乘除或加减来自动调用对应的处理函数,而 simplify() 函数负责递归剥括号,最终 calculator() 主函数将字符串格式化、去括号、计算并返回浮点数结果。
放上完整代码,注意正则表达式拆分的细节和符号处理的边界情况:
import re
def eq_format(eq):
'''
将算术字符串拆分为列表,例如 '1-2*3' -> ['1','-','2','*','3']
'''
format_list = re.findall(r'[\d.]+|[()+\-*/]', eq)
return format_list
def change(eq, count):
'''
处理列表中连续出现的 '-', '- -' 等符号问题
比如 ['-', '-', '2'] -> ['+', '2']
'''
if eq[count] == '-':
if eq[count-1] == '-':
eq[count-1] = '+'
del eq[count]
elif eq[count-1] == '+':
eq[count-1] = '-'
del eq[count]
return eq
def deal_multiplication_division(eq):
'''
递归处理所有乘除运算
'''
count = 0
while count < len(eq):
if eq[count] == '*':
if eq[count+1] != '-':
eq[count-1] = float(eq[count-1]) * float(eq[count+1])
del eq[count]
del eq[count]
else:
eq[count] = float(eq[count-1]) * float(eq[count+2])
eq[count-1] = '-'
del eq[count+1]
del eq[count+1]
eq = change(eq, count-1)
return deal_multiplication_division(eq)
elif eq[count] == '/':
if eq[count+1] != '-':
eq[count-1] = float(eq[count-1]) / float(eq[count+1])
del eq[count]
del eq[count]
else:
eq[count] = float(eq[count-1]) / float(eq[count+2])
eq[count-1] = '-'
del eq[count+1]
del eq[count+1]
eq = change(eq, count-1)
return deal_multiplication_division(eq)
count += 1
return eq
def deal_plus_minus(eq):
'''
处理加减运算,返回最终结果的列表形式
'''
if eq[0] != '-':
total = float(eq[0])
else:
total = 0.0
count = 0
for i in eq:
if i == '-':
total -= float(eq[count+1])
elif i == '+':
total += float(eq[count+1])
count += 1
if total >= 0:
return [str(total)]
else:
return ['-', str(-total)]
def calculate(s_eq):
'''
不带括号的列表,先乘除后加减
'''
if '*' in s_eq or '/' in s_eq:
s_eq = deal_multiplication_division(s_eq)
if '+' in s_eq or '-' in s_eq:
s_eq = deal_plus_minus(s_eq)
return s_eq
def simplify(format_list):
'''
递归去除所有括号
'''
bracket = 0
count = 0
while count < len(format_list):
if format_list[count] == '(':
bracket = count
elif format_list[count] == ')':
temp = format_list[bracket+1 : count]
new_temp = calculate(temp)
format_list = format_list[:bracket] + new_temp + format_list[count+1:]
format_list = change(format_list, bracket)
return simplify(format_list)
count += 1
return format_list
def calculator(eq):
format_list = eq_format(eq)
s_eq = simplify(format_list)
ans = calculate(s_eq)
if len(ans) == 2:
return -float(ans[1])
else:
return float(ans[0])
if __name__ == '__main__':
equation = '1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'
ans = calculator(equation)
print('eval运算结果:', eval(equation))
print('程序运算结果:', ans)

参考链接:
link