I'm wondering if there exists a python module that would allow me to do something like this:
x = MagicNumber()
x.value = 3
y = 2 * (x + 2) ** 2 - 8
print y # 42
x.value = 2
print y # 24
So MagicNumber
would implement all the special operator methods, and they would all return instances of MagicNumber, while keeping track of what operations are performed. Is there such a class?
EDIT: clarification
I want to use this in a module that should remember a lot of parameters of some arbitrary calculation that the user wishes to perform. So the user will set the parameters and then use them to produce his result. Then if he decides he'd like to alter a parameter, the change is reflected in his result immediately. So a very simplified usage session with only one parameter instance would look like:
p = MyParams()
p.distance = 13.4 # I use __getattr__ and __setattr__ such that
p.speed = 3.14 # __getattr__ returns MagicNumber instances
time = p.distance / p.speed
EDIT 2: more clarification
Okay, I'll do what I should have done from the start. I'll provide context.
You are an engineer and you're to produce a LaTeX document detailing the workings and properties of some prototype gadget. It is a task you'll do repeatedly for different prototypes. You write a small LaTeX python interface. For each prototype you create a python module that generates the requisite document. In it you type out the LaTeX code while calculating variables as they are needed, so that the calculations are in context. After a while you notice two problems:
Out of this problem comes the original question.
Something like this?
import operator
MAKE_BINARY = lambda opfn : lambda self,other : BinaryOp(self, asMagicNumber(other), opfn)
MAKE_RBINARY = lambda opfn : lambda self,other : BinaryOp(asMagicNumber(other), self, opfn)
class MagicNumber(object):
__add__ = MAKE_BINARY(operator.add)
__sub__ = MAKE_BINARY(operator.sub)
__mul__ = MAKE_BINARY(operator.mul)
__radd__ = MAKE_RBINARY(operator.add)
__rsub__ = MAKE_RBINARY(operator.sub)
__rmul__ = MAKE_RBINARY(operator.mul)
# __div__ = MAKE_BINARY(operator.div)
# __rdiv__ = MAKE_RBINARY(operator.div)
__truediv__ = MAKE_BINARY(operator.truediv)
__rtruediv__ = MAKE_RBINARY(operator.truediv)
__floordiv__ = MAKE_BINARY(operator.floordiv)
__rfloordiv__ = MAKE_RBINARY(operator.floordiv)
def __neg__(self, other):
return UnaryOp(self, lambda x : -x)
@property
def value(self):
return self.eval()
class Constant(MagicNumber):
def __init__(self, value):
self.value_ = value
def eval(self):
return self.value_
class Parameter(Constant):
def __init__(self):
super(Parameter, self).__init__(0.0)
def setValue(self, v):
self.value_ = v
value = property(fset=setValue, fget=lambda self: self.value_)
class BinaryOp(MagicNumber):
def __init__(self, op1, op2, operation):
self.op1 = op1
self.op2 = op2
self.opn = operation
def eval(self):
return self.opn(self.op1.eval(), self.op2.eval())
class UnaryOp(MagicNumber):
def __init__(self, op1, operation):
self.op1 = op1
self.operation = operation
def eval(self):
return self.opn(self.op1.eval())
asMagicNumber = lambda x : x if isinstance(x, MagicNumber) else Constant(x)
And here it is in action:
x = Parameter()
# integer division
y = 2*x*x + 3*x - x//2
# or floating division
# y = 2*x*x + 3*x - x/2
x.value = 10
print(y.value)
# prints 225
x.value = 20
print(y.value)
# prints 850
# compute a series of x-y values for the function
print([(x.value, y.value) for x.value in range(5)])
# prints [(0, 0), (1, 5), (2, 13), (3, 26), (4, 42)]
You could give sympy, a computer algebra system written in Python, give a try.
E.g.
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> y = 2 * (x + 2) ** 2 - 8
>>> y
2*(x + 2)**2 - 8
>>> y.subs(x,3)
42
>>> y.subs(x,2)
24
Isn't this called a function
? This may sound like a simple answer, but I mean it sincerely.
def y(x):
return 2 * (x + 2) ** 2 - 8
Aren't you thinking in the wrong direction with this one?
To address the clarification:
class MyParams():
distance = 0.0
speed = 0.0
def __call__(self):
return self.distance / self.speed
p = MyParams()
p.distance = 13.4 # These are properties
p.speed = 3.14 # where __get__ returns MagicNumber instances
time = p() # 4.26
p.speed = 2.28
time = p() # 5.88
I guess I'm more in favor of this type of a solution, although I see the benefit in the sympy module. Preference, I guess.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With