I'm working on a calculator in python. Introductory college course, so I know some stuff, but I'm far from being an expert of any sorts.
This is a scientific/graphing calculator, so I'm parsing a whole string of input.
Say the user inputs integ(3*x+3,0,1) (integrate 3x+3 from 0 to 1). My integration function wants parameters f, a, and b. How do I make eval call integ(), passing the variable x, without trying to see x as a variable? Considering this is a calculator, I'm expecting the possibility of long convoluted input crap such as diff(integ(3*x+3,0,1)*x+5,5)*(3*5)/4 (diff being the differentiate function)
I've ready plenty about calling functions and such with eval. Basically my question is, how do I call a function with eval() and pass it variable parameters?
Code snippets follow.
evalvars = {}
evalvars['integ'] = integ
def integ(funct,a,b):
def f(x):
return map(lambda x: eval(funct),x)
return gauss(a,b,f) #function I wrote for gaussian quadrature integration
def evaluate():
equation = entry.get() #raw input from entry box
answer = eval(equation, evalvars)
When I run this with raw input 'integ(3*x+5,0,1)' I get NameError: name 'x' is not defined
Alternatively, if you have any helpful hints for parsing user input into function calls, it would be very helpful. This is turning out to be the hardest part of this little graphing calculator I'm making. (what a surprise)
2
Contributors
8
Replies
13 Hours
Discussion Span
2 Years Ago
Last Updated
9
Views
Question Answered
Related Article:Calling functions from other classes
is a solved Python discussion thread by Archenemie that has 6 replies and was last updated 2 years ago.
if I run diff() the way it's written, then it'll try to compile(integ(log(...))) and then eval() it. Unless I'm missing something obvious, it'll choke on this, right?
if I run diff() the way it's written, then it'll try to compile(integ(log(...))) and then eval() it. Unless I'm missing something obvious, it'll choke on this, right?
I tried to play with this code, which evaluates expressions in a fixed namespace, where symbols are added on demand. The function evaluator() take an expression as an argument and returns a function where the unknown variables are ordered alphabetically (for example x and y)
from math import sqrt
EVAL_NAMESPACE = dict()
def varnames(function):
return func.func_code.co_varnames
def evaluator(expression):
namespace = EVAL_NAMESPACE
code = compile(expression, 'expression', 'eval')
args = []
for name in (n for n in code.co_names if not n in EVAL_NAMESPACE):
if name in globals():
EVAL_NAMESPACE[name] = globals()[name]
else:
args.append(name)
args = tuple(sorted(args))
func = """
def function(%s):
return eval(code, namespace, locals())
""" % (", ".join(args))
exec func in locals()
function.__name__ = "evaluator(%s)" % repr(expression)
print "EVALUATOR:", function.__name__
return function
def foo(expression, a, b):
func = evaluator(expression)
return (func(a) + func(b)) * 0.5
def bar(expression, x):
func = evaluator(expression)
return func(x+1) - func(x)
def evaluate(expression, *args):
return evaluator(expression)(*args)
expr = 'foo("x**2 + x", bar("3*x+2", 2), 1)'
print evaluate(expr)
print evaluate("sqrt(evaluate('x**2+y**2', 3, 8))")
For a more complete solution, you should have a look in the ast module. The ast.parse function parses a python expression and returns an abstract syntax tree. You could examine this tree and build your evaluator on this tree.
import ast
node = ast.parse("diff(integ(x**2+y**2, 3, 2), 2)")
def rec_traverse(node):
for n in ast.iter_child_nodes(node):
yield node, n
for item in rec_traverse(n):
yield item
def label(node):
return node.__class__.__name__
from fastgraph import graph
graph(rec_traverse(node), label).draw("graph.png", prog="dot")
import webbrowser
webbrowser.open("graph.png")
from the nodes Name and BinOp and Call, you should be able to obtain the variable names and the function names, and define your own evaluator for the parsed expression. (Remark: in a graphviz representation, the children of a node are not ordered)
I tried to play with this code, which evaluates expressions in a fixed namespace, where symbols are added on demand. The function evaluator() take an expression as an argument and returns a function where the unknown variables are ordered alphabetically (for example x and y)
from math import sqrt
EVAL_NAMESPACE = dict()
def varnames(function):
return func.func_code.co_varnames
def evaluator(expression):
namespace = EVAL_NAMESPACE
code = compile(expression, 'expression', 'eval')
args = []
for name in (n for n in code.co_names if not n in EVAL_NAMESPACE):
if name in globals():
EVAL_NAMESPACE[name] = globals()[name]
else:
args.append(name)
args = tuple(sorted(args))
func = """
def function(%s):
return eval(code, namespace, locals())
""" % (", ".join(args))
exec func in locals()
function.__name__ = "evaluator(%s)" % repr(expression)
print "EVALUATOR:", function.__name__
return function
def foo(expression, a, b):
func = evaluator(expression)
return (func(a) + func(b)) * 0.5
def bar(expression, x):
func = evaluator(expression)
return func(x+1) - func(x)
def evaluate(expression, *args):
return evaluator(expression)(*args)
expr = 'foo("x**2 + x", bar("3*x+2", 2), 1)'
print evaluate(expr)
print evaluate("sqrt(evaluate('x**2+y**2', 3, 8))")
For a more complete solution, you should have a look in the ast module. The ast.parse function parses a python expression and returns an abstract syntax tree. You could examine this tree and build your evaluator on this tree.
This is really awesome, thanks. I was getting close with a combination of compile, exec, and eval, but this is really helpful. I'll definitely be looking into the ast module.
I understand abstractly that *args is passing the sequence args as (positional?) arguments, but how does it work/what is it called? I've been searching for this in Python docs, but there are a lot of syntaxes with asterisks.
This is really awesome, thanks. I was getting close with a combination of compile, exec, and eval, but this is really helpful. I'll definitely be looking into the ast module.
I understand abstractly that *args is passing the sequence args as (positional?) arguments, but how does it work/what is it called? I've been searching for this in Python docs, but there are a lot of syntaxes with asterisks.