1,105,288 Community Members

Calling functions with eval()

Member Avatar
iamdabobo
Newbie Poster
4 posts since Nov 2010
Reputation Points: 0 [?]
Q&As Helped to Solve: 0 [?]
Skill Endorsements: 0 [?]
 
0
 

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)

Member Avatar
Gribouillis
Posting Maven
3,452 posts since Jul 2008
Reputation Points: 1,140 [?]
Q&As Helped to Solve: 883 [?]
Skill Endorsements: 18 [?]
Moderator
 
0
 

You could write

def evaluator(funct, name='funct'):
    code = compile(funct, name,  'eval')
    def f(x):
        return eval(code, locals())
    f.__name__ = name
    return f

def integ(funct,a,b):
    return gauss(a, b, evaluator(funct))

print integ("3*x++5", 0, 1)

because the expression is evaluated in a dictionary where there is a variable named x (the locals() dict of the function f() above).

Member Avatar
iamdabobo
Newbie Poster
4 posts since Nov 2010
Reputation Points: 0 [?]
Q&As Helped to Solve: 0 [?]
Skill Endorsements: 0 [?]
 
0
 

Ah, you just edited your code for a question I was going to ask.

Now, looking at that, how do I make it deal with nested functions? Or how do I parse the input to extract the nested functions?

Member Avatar
Gribouillis
Posting Maven
3,452 posts since Jul 2008
Reputation Points: 1,140 [?]
Q&As Helped to Solve: 883 [?]
Skill Endorsements: 18 [?]
Moderator
 
0
 

Ah, you just edited your code for a question I was going to ask.

Now, looking at that, how do I make it deal with nested functions? Or how do I parse the input to extract the nested functions?

what do you mean by nested functions ?

Member Avatar
iamdabobo
Newbie Poster
4 posts since Nov 2010
Reputation Points: 0 [?]
Q&As Helped to Solve: 0 [?]
Skill Endorsements: 0 [?]
 
0
 

what do you mean by nested functions ?

diff(integ(log(anotherCalculatorFunction())))

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?

Member Avatar
Gribouillis
Posting Maven
3,452 posts since Jul 2008
Reputation Points: 1,140 [?]
Q&As Helped to Solve: 883 [?]
Skill Endorsements: 18 [?]
Moderator
 
1
 

diff(integ(log(anotherCalculatorFunction())))

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.

Member Avatar
Gribouillis
Posting Maven
3,452 posts since Jul 2008
Reputation Points: 1,140 [?]
Q&As Helped to Solve: 883 [?]
Skill Endorsements: 18 [?]
Moderator
 
1
 

For example, using this code snippet http://www.daniweb.com/code/snippet323792.html and the ast module, I obtain the following graph for the parsing of an expression

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)

Attachments graph.png 30.31KB
Member Avatar
iamdabobo
Newbie Poster
4 posts since Nov 2010
Reputation Points: 0 [?]
Q&As Helped to Solve: 0 [?]
Skill Endorsements: 0 [?]
 
0
 

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.

One question:

def evaluate(expression, *args):
    return evaluator(expression)(*args)

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.

Member Avatar
Gribouillis
Posting Maven
3,452 posts since Jul 2008
Reputation Points: 1,140 [?]
Q&As Helped to Solve: 883 [?]
Skill Endorsements: 18 [?]
Moderator
 
0
 

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.

One question:

def evaluate(expression, *args):
    return evaluator(expression)(*args)

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.

It's described in the syntax of function calls in the python documentation http://docs.python.org/reference/expressions.html#calls

Question Answered as of 3 Years Ago by Gribouillis
You
This question has already been solved: Start a new discussion instead
Post:
Start New Discussion
Tags Related to this Article