Calculator in Python for dummies
17th of December 2007
I need a mini calculator in my web app so that people can enter basic mathematical expressions instead of having to work it out themselfs and then enter the result in the input box. I want them to be able to enter "3*2" or "110/3" without having to do the math first. I want this to work like a pocket calculator such that 110/3 returns a 36.6666666667 and not 36 like pure Python arithmetic would. Here's the solution which works but works like Python:
return eval(expr, dict(__builtins__=None), symbols)
def calc(expr):
return safe_eval(expr, vars(math))
assert calc('3*2')==6
assert calc('12.12 + 3.75 - 10*0.5')==10.87
assert calc('110/3')==36
But to make it work like non-Python-geek users would expect it I ended up with the following solution which also adds a few more bells and whistles:
import re
integers_regex = re.compile(r'\b[\d\.]+\b')
def calc(expr, advanced=False):
def safe_eval(expr, symbols={}):
return eval(expr, dict(__builtins__=None), symbols)
def whole_number_to_float(match):
group = match.group()
if group.find('.') == -1:
return group + '.0'
return group
expr = expr.replace('^','**')
expr = integers_regex.sub(whole_number_to_float, expr)
if advanced:
return safe_eval(expr, vars(math))
else:
return safe_eval(expr)
def test():
print calc("147.43 - 40") # 107.43
print calc('110/3') # 36.6666666667
print calc('110/3.0') # 36.6666666667
print calc('(10-(3+5))^2') # 4.0
print calc('sys.exit(100)') # None
print calc('a+b') # None
print calc('(3+10))') # None
print calc('del expr') # None
print calc('cos(2*pi)') # None
print calc('pow(3,2)', advanced=True) # 9.0
print calc('cos(2*pi)', advanced=True) # 1.0
What this does is that it replaces whole numbers into floating point looking numbers before the expression is evaluated. It also replaces ** with ^ as an alias because I think most non-Python people expect 10^2 to be 100.
I haven't put this into production yet. I'm still playing around with it to get a feel for how it could work and what the implications might be. There is of course more work needed to wrap this with try-except statements so that dodgy attempts are captured correctly.
Comment
Show all 16 commentsCommenting is currently disabled in Mobile version