Reputation: 383
So, my code is like this:
def func(s,x):
return eval(s.replace('x',x)
#Example:
>> func('x**2 + 3*x',1)
4
The first argument of the function func
must be a string because the function eval
accepts only string or code objects. However, I'd like to use this function in a kind of calculator, where the user types for example 2 + sin(2*pi-0.15) + func(1.8*x-32,273)
and gets the answer of the expression, and it's annoying always to have to write the quotes before in the expression inside func()
.
Is there a way to make python understands the s
argument is always a string, even when it's not between quotes?
Upvotes: 1
Views: 4189
Reputation: 362147
No, it is not possible. You can't intercept the Python interpreter before it parses and evaluates 1.8*x-32
.
Using eval
as a glorified calculator is a highly questionable idea. The user could pass in all kinds of malicious Python code. If you're going to do it, you should provide as minimal an environment as possible for the code to run in. Pass in your own globals dict
containing only the variables the user is allowed to reference.
return eval(s, {'x': x})
Besides being safer, this is also a better way to substitute x
into the expression.
Upvotes: 2
Reputation: 10663
You can write an interpreter:
import code
def readfunc(prompt):
raw = input(prompt)
if raw.count(',')!=1:
print('Bad expression: {}'.format(raw))
return ''
s, x = raw.split(',')
return '''x={}; {}'''.format(x, s)
code.interact('Calc 0.1', readfunc)
Upvotes: 0
Reputation: 353559
In a word: no, if I understand you.
In a few more, you can sort of get around the problem by making x
be a special object. This is how the Python math library SymPy works. For example:
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> x**2+3*x
x**2 + 3*x
>>> (x**2+3*x).subs(x,1)
4
There's even a handy function to turn strings into sympy objects:
>>> from sympy import sympify, pi
>>> sympify("x**2 - sin(x)")
x**2 - sin(x)
>>> _.subs(x, pi)
pi**2
All the warnings about untrusted user input hold. [I'm too lazy to check whether or not eval
or exec
is used on the sympify
code path, and as they say, every weapon is loaded, even the unloaded ones.]
Upvotes: 1
Reputation: 298552
You could have it handle both cases:
def func(s, x=0):
if isinstance(s, basestring):
# x is in the scope, so you don't need to replace the string
return eval(s)
else:
return s
And the output:
>>> from math import *
>>> func('2 + sin(2*pi-0.15) + func(1.8*x-32,273)')
-30.1494381324736
>>> func('x**2 + 3*x', 1)
4
Caution: eval
can do more than just add numbers. I can type __import__('os').system('rm /your/homework.doc')
and your calculator will delete your homework.
Upvotes: 1