44

How can I (easily) take a string such as "sin(x)*x^2" which might be entered by a user at runtime and produce a Python function that could be evaluated for any value of x?

6 Answers 6

53

Python's own internal compiler can parse this, if you use Python notation.

If your change the notation slightly, you'll be happier.

import compiler
eq= "sin(x)*x**2"
ast= compiler.parse( eq )

You get an abstract syntax tree that you can work with.

Sign up to request clarification or add additional context in comments.

10 Comments

@Don: You don't need to use the syntax tree. Use the original function. eval("sin(x)*x**2") after setting x and using from math import *.
import compiler doesn't work with Python 3.3 Could you please update your answer + tags to make sure the reader know which one it is about. It's a good question and should have the relevant tags covered.
@hagubear the replacement of the compiler module for python 3 is described here: stackoverflow.com/questions/909092/…
Do NOT use eval with data coming from an external source (e.g. web) as that is an easy attack vector.
The compiler module is deprecated since Python 2.6. This answer is obsolete
|
34

EDIT parser is deprecated in Python 3.9: https://docs.python.org/3/whatsnew/3.9.html#new-parser

You can use Python parser:

import parser
from math import sin

formula = "sin(x)*x**2"
code = parser.expr(formula).compile()
x = 10
print(eval(code))

It performs better than pure eval and, of course, avoids code injection!

5 Comments

One important thing to note is that using pure eval() on user input can be very dangerous.
@devxeq I mean that it won't accept such things as formula = "os.system('format C:')" :)
@Don Fair enough. :)
I'm not sure how this is any better than using eval on the string itself. It will still happily execute all code, not just formulas. Including bad things like os.system calls. >>> eval(parser.expr("os.system('echo evil syscall')").compile()) evil syscall
parser is deprecated in python 3.9 docs.python.org/3/whatsnew/3.9.html#new-parser
15
 f = parser.parse('sin(x)*x^2').to_pyfunc()

Where parser could be defined using PLY, pyparsing, builtin tokenizer, parser, ast.

Don't use eval on user input.

3 Comments

eval on user input is indeed bad.
Could you be more specific? I have tried to import parser module in Python 3.8 but it complains: Module 'parser' has no attribute 'parse'
@s.ouchene: "parser" here is just a stand-in for a custom module that implements the corresponding functionality (it is not literally stdlib's parser module). Here's an example for arithmetic expressions
10

pyparsing might do what you want (http://pyparsing.wikispaces.com/) especially if the strings are from an untrusted source.

See also http://pyparsing.wikispaces.com/file/view/fourFn.py for a fairly full-featured calculator built with it.

2 Comments

The links are no longer avaible on wikispaces.
Pyparsing is no longer hosted on wikispaces.com. Go to github.com/pyparsing/pyparsing
3

To emphasize J.F. Sebastian's advice, 'eval' and even the 'compiler' solutions can be open to subtle security holes. How trustworthy is the input? With 'compiler' you can at least filter out things like getattr lookups from the AST, but I've found it's easier to use PLY or pyparsing for this sort of thing than it is to secure the result of letting Python help out.

Also, 'compiler' is clumsy and hard to use. It's deprecated and removed in 3.0. You should use the 'ast' module (added in 2.6, available in 2.5 as '_ast').

Comments

0

Sage is intended as matlab replacement and in intro videos it's demonstrated how similar to yours cases are handled. They seem to be supporting a wide range of approaches. Since the code is open-source you could browse and see for yourself how the authors handle such cases.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.