David Sousa
David Sousa

Reputation: 383

Force function argument to be a string

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

Answers (4)

John Kugelman
John Kugelman

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

Kabie
Kabie

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

DSM
DSM

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

Blender
Blender

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

Related Questions