Reputation: 1462
How would one allow the user to input a statement such as "math.sin(x)**2" and calculate the answer within python code.
In telling me the answer please explain why both exec() compile() are not producing the desired result.
import math
def getValue(function, x):
function = "val = " + function
#compile(function, '', 'exec')
exec(function)
print(val)
function = input("Enter a function f(x):\n")
getValue(function, 10)
Much Appreciated!
Upvotes: 2
Views: 115
Reputation: 103754
To answer your question, use eval:
>>> eval('math.sin(1)**2')
0.7080734182735712
exec is working, but you are not retrieving the result. Notice:
>>> val
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'val' is not defined
>>> exec('val=math.sin(1)**2')
>>> val
0.7080734182735712
So, using eval
instead of exec
works like so:
def getValue(function, x):
function = "{}({})".format(function, x)
print(function)
val=eval(function)
print(val)
That said -- it is considered an extreme security risk to execute arbitrary user code.
If you are building a calculator, a safer approach you might consider using SymPy or building your own parser using something like PyParsing (which is used in SymPy)
An example PyParsing calculator:
import sys
import operator
from pyparsing import nums, oneOf, Word, Literal, Suppress
from pyparsing import ParseException, Forward, Group
op_map = { '*' : operator.mul,\
'+' : operator.add,\
'/' : operator.div,\
'-' : operator.sub}
exp = Forward()
number = Word(nums).setParseAction(lambda s, l, t: int(t[0]))
lparen = Literal('(').suppress()
rparen = Literal(')').suppress()
op = oneOf('+ - * /').setResultsName('op').setParseAction(lambda s, l, t: op_map[t[0]])
exp << Group(lparen + op + (number | exp) + (number | exp) + rparen)
def processArg(arg):
if isinstance(arg, int):
return arg
else:
return processList(arg)
def processList(lst):
args = [processArg(x) for x in lst[1:]]
return lst.op(args[0], args[1])
def handleLine(line):
result = exp.parseString(line)
return processList(result[0])
while True:
try:
print handleLine(raw_input('> '))
except ParseException, e:
print >>sys.stderr,\
"Syntax error at position %d: %s" % (e.col, e.line)
except ZeroDivisionError:
print >>sys.stderr,\
"Division by zero error"
Which can easily be extended to include other functions.
Upvotes: 4