Nathan Bush
Nathan Bush

Reputation: 1461

Passing arithmatic expressions in python without evaluating them

I've searched and couldn't find a direct answer for my question, so apologies if this has already been posted/answered before. I'm working in python and i need to pass expressions that contain variables, but i don't want them to be immediately evaluated.

For example:

r = x*y

I want the program to remember that in order to calculate r, it needs to multiply x and y instead of explicitly calculating it at the time. I've tried using:

x = None
y = None
r = x*y

But this doesn't allow operations on the variables. I've managed it using strings and then using "eval" but its not a very elegant solution, and its also painfully slow. Is there a better way of doing this?

Upvotes: 2

Views: 760

Answers (2)

Noctis Skytower
Noctis Skytower

Reputation: 22041

You might look at the source code of the Math Evaluator utility to get an idea how to create expressions for evaluation later on. This demonstration on Ideone.com helps to show what the code can accomplish.

if __name__ == '__main__':
    # Run a simple demo that shows evaluator's capability.
    namespace = {}
    expression = tokens('x * y -> r')
    print expression
    evaluate('2 -> x; 3 -> y', namespace)
    expression.evaluate(namespace)
    print 'r =', namespace['r']
    alternate = Operation(Operation(Variable('x'), '+', Variable('y')), '->', Variable('r'))
    print alternate
    alternate.evaluate(namespace)
    print 'r =', namespace['r']

Upvotes: 0

mgilson
mgilson

Reputation: 310287

You could use a lambda expression:

>>> x = None
>>> y = None
>>> r = lambda : x*y
>>> r()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for *: 'NoneType' and 'NoneType'
>>> x = 1
>>> y = 2
>>> r()
2

You could even get a little more fancy with a class:

class DeferredEval(object):
    def __init__(self,func):
        self.func = func

    def __call__(self):
        return self.func()

    def __add__(self,other):
        return self.func() + other

    def __radd__(self,other):
        return other + self.func()


x = None
y = None
r = DeferredEval(lambda:x*y)

try:
    a = 1 + r
except TypeError as err:
    print "Oops, can't calculate r yet -- Reason:",err

x = 1
y = 2
print 1 + r
print r + 1

with the output:

Oops, can't calculate r yet -- Reason: unsupported operand type(s) for *: 'NoneType' and 'NoneType'
3
3

Of course, here you'll need to add a whole bunch more methods if you want to do things that aren't addition, subtraction, ... Of course, then you have to actually call r in order to get your result -- but that's not so bad is it?

Upvotes: 4

Related Questions