I am to build a class that accepts a series of inputs via the constructor method, then perform a calculation with calculate() using these parameters. The trick here is that these parameters might be available sometimes and other times might not. There however, is a given equation between the variables, such that the missing ones can be calculated from the equations. Here is an example:
I know that:
a = b * c - d
c = e/f
I am to calculate always a+b+c+d+e+f
Here is what I have so far:
class Calculation:
def __init__(self, **kwargs):
for parameter, value in kwargs.items():
setattr(self, '_'.format(parameter), value)
#property
def a(self):
try:
return self._a
except AttributeError:
return self._b * self._c - self._d
#property
def b(self):
try:
return self._b
except AttributeError:
return (self._a + self._d) / self._c
... // same for all a,b,c,d,e,f
def calculate(self):
return sum(self.a+self.b+self.c+self.d+self.e+self.f)
then use as:
c = Calculation(e=4,f=6,b=7,d=2)
c.calculate()
however, some other time might have other variables like:
c = Calculation(b=5,c=6,d=7,e=3,f=6)
c.calculate()
My question is: What would be a good design pattern to use in my case? So far, it seems a bit redundant to make a #property for all variables. The problem it must solve is to accept any variables (minimum for which calculation is possible) and based on the equation I have, figure out the rest, needed for calculation.
This seems like a good candidate for the getattr function. You can store the keyword arguments directly in the class and use that dictionary to either return a known parameter as attribute or infer an unspecified value "on the fly" based on other formulas that you know of:
class Calculation:
def __init__(self, **kwargs):
self.params = kwargs
self.inferred = {
"a" : lambda: self.b * self.c - self.d,
"c" : lambda: self.e / self.f,
"result": lambda: self.a+self.b+self.c+self.d+self.e+self.f
}
def __getattr__(self, name):
if name in self.params:
return self.params[name]
if name in self.inferred:
value = self.inferred[name]()
self.params[name] = value
return value
r = Calculation(b=1,d=3,e=45,f=9).result
print(r) # 65.0 (c->45/9->5, a->1*5-3->2)
Note that, if you have very complicated calculations for some of the parameters, you can use functions of the class as the implementation of the lambdas in the self.inferred dictionary.
If you're going to use this pattern for many formulas, you might want to centralize the boilerplate code in a base class. This will reduce the work needed for new calculation classes to only having to implement the inferred() function.:
class SmartCalc:
def __init__(self, **kwargs):
self.params = kwargs
def __getattr__(self, name):
if name in self.params:
return self.params[name]
if name in self.inferred():
value = self.inferred()[name]()
self.params[name] = value
return value
class Calculation(SmartCalc):
def inferred(self):
return {
"a" : lambda: self.b * self.c - self.d,
"b" : lambda: (self.a+self.d)/self.c,
"c" : lambda: self.e / self.f,
"d" : lambda: self.c * self.b - self.a,
"e" : lambda: self.f * self.c,
"f" : lambda: self.e / self.c,
"result": lambda: self.a+self.b+self.c+self.d+self.e+self.f
}
With enough content in inferred(), you can even use this approach to obtain any value from a combination of the others:
valueF = Calculation(a=2,b=1,c=5,d=3,e=45,result=65).f
print(valueF) # 9.0
EDIT
If you want to make this even more sophisticated, you can improve getattr to allow specification of dependencies in the inferred() dictionary.
For example:
class SmartCalc:
def __init__(self, **kwargs):
self.params = kwargs
def __getattr__(self, name):
if name in self.params:
return self.params[name]
if name in self.inferred():
calc = self.inferred()[name]
if isinstance(calc,dict):
for names,subCalc in calc.items():
if isinstance(names,str): names = [names]
if all(name in self.params for name in names):
calc = subCalc; break
value = calc()
self.params[name] = value
return value
import math
class BodyMassIndex(SmartCalc):
def inferred(self):
return {
"heightM" : { "heightInches": lambda: self.heightInches * 0.0254,
("bmi","weightKg"): lambda: math.sqrt(self.weightKg/self.bmi),
("bmi","weightLb"): lambda: math.sqrt(self.weightKg/self.bmi)
},
"heightInches" : lambda: self.heightM / 0.0254,
"weightKg" : { "weightLb": lambda: self.weightLb / 2.20462,
("bmi","heightM"): lambda: self.heightM**2*self.bmi,
("bmi","heightInches"): lambda: self.heightM**2*self.bmi
},
"weightLb" : lambda: self.weightKg * 2.20462,
"bmi" : lambda: self.weightKg / (self.heightM**2)
}
bmi = BodyMassIndex(heightM=1.75,weightKg=130).bmi
print(bmi) # 42.44897959183673
height = BodyMassIndex(bmi=42.45,weightKg=130).heightInches
print(height) # 68.8968097135968 (1.75 Meters)
EDIT2
A similar class could be designed to process formulas expressed as text. This would allow a basic form of term solver using a newton-raphson iterative approximation (at least for 1 degree polynomial equations):
class SmartFormula:
def __init__(self, **kwargs):
self.params = kwargs
self.moreToSolve = True
self.precision = 0.000001
self.maxIterations = 10000
def __getattr__(self, name):
self.resolve()
if name in self.params: return self.params[name]
def resolve(self):
while self.moreToSolve:
self.moreToSolve = False
for formula in self.formulas():
param = formula.split("=",1)[0].strip()
if param in self.params: continue
if "?" in formula:
self.useNewtonRaphson(param)
continue
try:
exec(formula,globals(),self.params)
self.moreToSolve = True
except: pass
def useNewtonRaphson(self,name):
for formula in self.formulas():
source,calc = [s.strip() for s in formula.split("=",1)]
if name not in calc: continue
if source not in self.params: continue
simDict = self.params.copy()
target = self.params[source]
value = target
try:
for _ in range(self.maxIterations):
simDict[name] = value
exec(formula,globals(),simDict)
result = simDict[source]
resultDelta = target-result
value += value*resultDelta/result/2
if abs(resultDelta) < self.precision/2 :
self.params[name] = round(simDict[name]/self.precision)*self.precision
self.moreToSolve = True
return
except: continue
With this approach the BodyMassIndex calculator would be easier to read:
import math
class BodyMassIndex(SmartFormula):
def formulas(self):
return [
"heightM = heightInches * 0.0254",
"heightM = ?", # use Newton-Raphson solver.
"heightInches = ?",
"weightKg = weightLb / 2.20462",
"weightKg = heightM**2*bmi",
"weightLb = ?",
"bmi = weightKg / (heightM**2)"
]
This lets you obtain/use terms for which the calculation formula is not explicitly stated in the list (e.g. heightInches computed from heightM which is computed from bmi and weightKg):
height = BodyMassIndex(bmi=42.45,weightKg=130).heightInches
print(height) # 68.8968097135968 (1.75 Meters)
Note: The formulas are expressed as text and executed using eval() which may be much slower than the other solution. Also, the Newton-Raphson algorithm is OK for linear equations but has its limitations for curves that have a mix of positive and negative slopes. For example, I had to include the weightKg = heightM**2*bmi formula because obtaining weightKg based on bmi = weightKg/(heightM**2) needs to solve a y = 1/x^2 equation which Newton-Raphson can't seem to handle.
Here's an example using your original problem:
class OP(SmartFormula):
def formulas(self):
return [
"a = b * c - d",
"b = ?",
"c = e/f",
"d = ?",
"e = ?",
"f = ?",
"result = a+b+c+d+e+f"
]
r = OP(b=1,d=3,e=45,f=9).result
print(r) # 65.0
f = OP(a=2,c=5,d=3,e=45,result=65).f
print(f) # 9.0
class ABCD(SmartFormula):
def formulas(self) : return ["a=b+c*d","b=?","c=?","d=?"]
#property
def someProperty(self): return "Found it!"
abcd = ABCD(a=5,b=2,c=3)
print(abcd.d) # 1.0
print(abcd.someProperty) # Found it!
print(abcd.moreToSolve) # False
Just precompute the missing values in __init__ (and since you know what the 5 values are, be explicit rather than trying to compress the code using kwargs):
# Note: Make all 6 keyword-only arguments
def __init__(self, *, a=None, b=None, c=None, d=None, e=None, f=None):
if a is None:
a = b * c - d
if c is None:
c = e / f
self.sum = a + b + c + d + e + f
def calculate(self):
return self.sum
[New Answer to complement previous one]
I felt my answer was getting too big so I'm adding this improved solution in a separate one.
This is a basic algebra solver to for simple equations that will output an assignment statement for a different term of the input equation:
For example:
solveFor("d","a=b+c/d") # --> 'd=c/(a-b)'
With this function, you can further improve the SmartFormula class by attempting to use algebra before reverting to Newton-Raphson. This will provide more reliable results when the equation is simple enough for the solveFor() function.
The solveFor() function can solve the equation for any term that appears only once in the formula. It will "understand" the calculation as long as the components to solve are only related with basic operations (+, -, *, /, **). Any group in parentheses that does not contain the target term will be processed "as is" without being further interpreted. This allows you to place complex functions/operators in parentheses so that other terms can be solved even in the presence of these special calculations.
import re
from itertools import accumulate
def findGroups(expression):
levels = list(accumulate(int(c=="(")-int(c==")") for c in expression))
groups = "".join([c,"\n"][lv==0] for c,lv in zip(expression,levels)).split("\n")
groups = [ g+")" for g in groups if g ]
return sorted(groups,key=len,reverse=True)
functionMap = [("sin","asin"),("cos","acos"),("tan","atan"),("log10","10**"),("exp","log")]
functionMap += [ (b,a) for a,b in functionMap ]
def solveFor(term,equation):
equation = equation.replace(" ","").replace("**","†")
termIn = re.compile(f"(^|\\W){term}($|\\W)")
if len(termIn.findall(equation)) != 1: return None
left,right = equation.split("=",1)
if termIn.search(right): left,right = right,left
groups = { f"#{i}#":group for i,group in enumerate(findGroups(left)) }
for gid,group in groups.items(): left = left.replace(group,gid)
termGroup = next((gid for gid,group in groups.items() if termIn.search(group)),"##" )
def moveTerms(leftSide,rightSide,oper,invOper):
keepLeft = None
for i,x in enumerate(leftSide.split(oper)):
if termGroup in x or termIn.search(x):
keepLeft = x; continue
x = x or "0"
if any(op in x for op in "+-*/"): x = "("+x+")"
rightSide = invOper[i>0].replace("{r}",rightSide).replace("{x}",x)
return keepLeft, rightSide
def moveFunction(leftSide,rightSide,func,invFunc):
fn = leftSide.split("#",1)[0]
if fn.split(".")[-1] == func:
return leftSide[len(fn):],fn.replace(func,invFunc)
return leftSide,rightSide
left,right = moveTerms(left,right,"+",["{r}-{x}"]*2)
left,right = moveTerms(left,right,"-",["{x}-{r}","{r}+{x}"])
left,right = moveTerms(left,right,"*",["({r})/{x}"]*2)
left,right = moveTerms(left,right,"/",["{x}/({r})","({r})*{x}"])
left,right = moveTerms(left,right,"†",["log({r})/log({x})","({r})†(1/{x})"])
for func,invFunc in functionMap:
left,right = moveFunction(left,right,func,f"{invFunc}({right})")
for sqrFunc in ["math.sqrt","sqrt"]:
left,right = moveFunction(left,right,sqrFunc,f"({right})**2")
for gid,group in groups.items(): right = right.replace(gid,group)
if left == termGroup:
subEquation = groups[termGroup][1:-1]+"="+right
return solveFor(term,subEquation)
if left != term: return None
solution = f"{left}={right}".replace("†","**")
# expression clen-up
solution = re.sub(r"(?<!\w)(0\-)","-",solution)
solution = re.sub(r"1/\(1/(\w)\)",r"\g<1>",solution)
solution = re.sub(r"\(\(([^\(]*)\)\)",r"(\g<1>)",solution)
solution = re.sub(r"(?<!\w)\((\w*)\)",r"\g<1>",solution)
return solution
Example Uses:
solveFor("x","y=(a+b)*x-(math.sin(1.5)/322)") # 'x=(y+(math.sin(1.5)/322))/(a+b)'
solveFor("a","q=(a**2+b**2)*(c-d)**2") # 'a=(q/(c-d)**2-b**2)**(1/2)'
solveFor("a","c=(a**2+b**2)**(1/2)") # 'a=(c**2-b**2)**(1/2)'
solveFor("a","x=((a+b)*c-d)*(23+y)") # 'a=(x/(23+y)+d)/c-b'
sa = solveFor("a","y=-sin((x)-sqrt(a))") # 'a=(x-asin(-y))**2'
sx = solveFor("x",sa) # 'x=a**(1/2)+asin(-y)'
sy = solveFor("y",sx) # 'y=-sin(x-a**(1/2))'
Note that you can probably find much better algebra 'solvers' out there, this is just a simple/naive solution.
Here is an improved version of the SmartFormula class that uses solveFor() to attempt an algebra solution before reverting to Newton-Raphson approximations:
class SmartFormula:
def __init__(self, **kwargs):
self.params = kwargs
self.precision = 0.000001
self.maxIterations = 10000
self._formulas = [(f.split("=",1)[0].strip(),f) for f in self.formulas()]
terms = set(term for _,f in self._formulas for term in re.findall(r"\w+\(?",f) )
terms = [ term for term in terms if "(" not in term and not term.isdigit() ]
self._formulas += [ (term,f"{term}=solve('{term}')") for term in terms]
self(**kwargs)
def __getattr__(self, name):
if name in self.params: return self.params[name]
def __call__(self, **kwargs):
self.params = kwargs
self.moreToSolve = True
self.params["solve"] = lambda n: self.autoSolve(n)
self.resolve()
return self.params.get(self._formulas[0][0],None)
def resolve(self):
while self.moreToSolve:
self.moreToSolve = False
for param,formula in self._formulas:
if self.params.get(param,None) is not None: continue
try:
exec(formula,globals(),self.params)
if self.params.get(param,None) is not None:
self.moreToSolve = True
except: pass
def autoSolve(self, name):
for resolver in [self.algebra, self.newtonRaphson]:
for source,formula in self._formulas:
if self.params.get(source,None) is None:
continue
if not re.search(f"(^|\\W){name}($|\\W)",formula):
continue
resolver(name,source,formula)
if self.params.get(name,None) is not None:
return self.params[name]
def algebra(self, name, source, formula):
try: exec(solveFor(name,formula),globals(),self.params)
except: pass
def newtonRaphson(self, name, source,formula):
simDict = self.params.copy()
target = self.params[source]
value = target
for _ in range(self.maxIterations):
simDict[name] = value
try: exec(formula,globals(),simDict)
except: break
result = simDict[source]
resultDelta = target-result
if abs(resultDelta) < self.precision :
self.params[name] = round(value/self.precision/2)*self.precision*2
return
value += value*resultDelta/result/2
This allowed the example class (BodyMassIndex) to avoid specification of the "weightKg = heightM**2*bmi" calculation because the algebra solver can figure it out. The improved class also eliminates the need to indicate auto-solving term names ("term = ?").
import math
class BodyMassIndex(SmartFormula):
def formulas(self):
return [
"bmi = weightKg / (heightM**2)",
"heightM = heightInches * 0.0254",
"weightKg = weightLb / 2.20462"
]
bmi = BodyMassIndex()
print("bmi",bmi(heightM=1.75,weightKg=130)) # 42.44897959183673
print("weight",bmi.weightLb) # 286.6006 (130 Kg)
bmi(bmi=42.45,weightKg=130)
print("height",bmi.heightInches) # 68.8968097135968 (1.75 Meters)
For the original question, this is simple as can be:
class OP(SmartFormula):
def formulas(self):
return [
"result = a+b+c+d+e+f",
"a = b * c - d",
"c = e/f"
]
r = OP(b=1,d=3,e=45,f=9).result
print(r) # 65.0
f = OP(a=2,c=5,d=3,e=45,result=65).f
print(f) # 9.0
Newton-Raphson was not used in any of these calculations because the algebra solves them in priority before trying the approximations
Related
I have the following code:
class Player:
def __init__(self, N, V, F, L, T):
self._Name = N
self._VPs = V
self._Fuel = F
self._Lumber = L
self._PiecesInSupply = T
def AddPiecesInSupply(self, T):
modify = T+1
modify.T
return T
I have been given code and been asked to make changes. "self._PiecesInSupply = T" is a protected attribute. I have been asked to create a method to allow "self._PiecesInSupply = T" to be modified so I created the code:
def AddPiecesInSupply(self, T):
modify = T+1
modify.T
return T
This is how I call Player.AddPiecesInSupply
Player.AddPiecesInSupply(0)
however the value 0 is being passed to the parameter self not T. Any ideas on how to pass the value 0 to T?
Thanks
Maybe you want something like this:
class Player:
def __init__(self, N, V, F, L, T):
self._Name = N
self._VPs = V
self._Fuel = F
self._Lumber = L
self._PiecesInSupply = T
def AddPiecesInSupply(self, T):
self._PiecesInSupply += T
return T
player = Player('name','VPs',1,2,3)
player.AddPiecesInSupply(0)
print(player._PiecesInSupply)
class Student:
def __init__(self, chemistry, english, math):
self.chemistry = chemistry
self.english = english
self.math = math
def chemistry(self):
return self.chemistry
def english(self):
return self.english
def math(self):
return self.math
def tot(self):
return self.chemistry + self.english + self.math
answer = Student(100,20,30)
answer.tot()
print(answer.tot())
I want to know how to get 3 inputs in one variable. I've tried using "list" but it didn't work.
Explanation
You can use a list or tuple as one variable, for example with a function called add (adds two numbers):
def add(a, b):
return a + b
print(add(3, 8))
def add(nums):
return nums[0] + nums[1]
print(add([3,8])) # List
print(add((3,8))) # Tuple
Code:
For class:
class Student:
def __init__(self, subjects):
self.chemistry = subjects[0]
self.english = subjects[1]
self.math = subjects[2]
# Your Methods, consider renaming them
For initializing class:
answer = Student([100,20,30]) # Using a List
answer = Student((100,20,30)) # Using a Tuple
I am doing some coding in python and I've come across little problem :
I defined several version of functions and want to assign one of them in the __init__ function when I create my object. I need those function to have a self argument.
I found a way to do it but I cannot tell if I am using the language correctly.
In my_class.py
class my_class(object):
fun_1 = lambda self,val: self.attr + val
fun_2 = lambda self,val: self.attr - val
def __init__(self, attr, fun_version):
self.attr = attr
my_class.fun = my_class.__dict__['fun_' + str(fun_version)]
In test.py
import my_class
my_instance_1 = my_class.my_class(attr = 2, fun_version = 1)
print(my_instance_1.fun(1))
my_instance_2 = my_class.my_class(attr = 2, fun_version = 2)
print(my_instance_2.fun(1))
The result follows what is expected :
3
1
Is there more elegant way to do it ? I find awkward to define a class method in the __init__ function.
A simple way to achieve what you want is (class and test code in one file for simplicity):
class my_class(object):
fun_1 = lambda self,val: self.attr + val
fun_2 = lambda self,val: self.attr - val
def __init__(self, attr, fun_version):
self.attr = attr
self.fun = getattr(self, 'fun_' + str(fun_version))
my_instance_1 = my_class(attr = 2, fun_version = 1)
print(my_instance_1.fun(1))
my_instance_2 = my_class(attr = 2, fun_version = 2)
print(my_instance_2.fun(1))
#Proof that instances are independent:
print(my_instance_1.fun(1))
Prints:
3
1
3
If the variants of fun don't need instance attributes (are static methods) this can be written this way (it could also be written with lambda in some way similar to first variant but this will look ugly):
class my_class(object):
#staticmethod
def fun_1(val):
return 1 + val
#staticmethod
def fun_2(val):
return 2 + val
def __init__(self, attr, fun_version):
self.attr = attr
self.fun = getattr(my_class, 'fun_' + str(fun_version))
# or (as above):
# self.fun = getattr(self, 'fun_' + str(fun_version))
my_instance_1 = my_class(attr = 2, fun_version = 1)
print(my_instance_1.fun(1))
my_instance_2 = my_class(attr = 2, fun_version = 2)
print(my_instance_2.fun(1))
print(my_instance_1.fun(1))
Prints
2
3
2
I would like to have users enter specific values and then the system computes numerous results based on what these - My program is getting very complicated with just a few functions. I have included an example with 3 simple functions and 6 variables with the following relationships:
The Code I have is as follows:
class MyCalculator:
def __init__(self):
self.a = None
self.b = None
self.c = None
self.d = None
self.e = None
self.f = None
def set(self, field, val):
if field == "a": self.a = val
if field == "b": self.b = val
if field == "c": self.c = val
if field == "d": self.d = val
if field == "e": self.e = val
for i in range(10): # circle round a few times to ensure everything has computed
if self.a and self.b:
self.c = self.a * self.b
if self.a and self.c:
self.b = self.c / self.a
if self.b and self.c:
self.a = self.c / self.b
if self.b and self.d:
self.e = self.b + self.d
if self.e and self.b:
self.d = self.e - self.b
if self.e and self.d:
self.b = self.e - self.d
if self.c and self.e:
self.f = self.c / self.e
if self.f and self.e:
self.e = self.f * self.e
if self.f and self.c:
self.e = self.c / self.f
def status(self):
print(f"a = {self.a} b = {self.b} c = {self.c} d = {self.d} e = {self.e} f = {self.f} ")
Then If i run the following code:
example1 = MyCalculator()
example1.set("a", 5)
example1.set("e", 7)
example1.set("c", 2)
example1.status()
This will print out a = 5.0 b = 0.40000000000000036 c = 2.0000000000000018 d = 6.6 e = 7.0 f = 0.285714285714286
I would like a much simpler way to achieve the same result using something like sympy and numpy but so far I cant find anything that will work
There's a live version of this solution online you can try for yourself
Here's a complete solution that uses Sympy. All you need to do is enter your desired expressions in the exprStr tuple at the top of the MyCalculator definition, and then all of the dependency satisfaction stuff should take care of itself:
from sympy import S, solveset, Symbol
from sympy.parsing.sympy_parser import parse_expr
class MyCalculator:
# sympy assumes all expressions are set equal to zero
exprStr = (
'a*b - c',
'b + d - e',
'c/e - f'
)
# parse the expression strings into actual expressions
expr = tuple(parse_expr(es) for es in exprStr)
# create a dictionary to lookup expressions based on the symbols they depend on
exprDep = {}
for e in expr:
for s in e.free_symbols:
exprDep.setdefault(s, set()).add(e)
# create a set of the used symbols for input validation
validSymb = set(exprDep.keys())
def __init__(self, usefloat=False):
"""usefloat: if set, store values as standard Python floats (instead of the Sympy numeric types)
"""
self.vals = {}
self.numify = float if usefloat else lambda x: x
def set(self, symb, val, _exclude=None):
# ensure that symb is a sympy Symbol object
if isinstance(symb, str): symb = Symbol(symb)
if symb not in self.validSymb:
raise ValueError("Invalid input symbol.\n"
"symb: %s, validSymb: %s" % (symb, self.validSymb))
# initialize the set of excluded expressions, if needed
if _exclude is None: _exclude = set()
# record the updated value of symb
self.vals[symb] = self.numify(val)
# loop over all of the expressions that depend on symb
for e in self.exprDep[symb]:
if e in _exclude:
# we've already calculated an update for e in an earlier recursion, skip it
continue
# mark that e should be skipped in future recursions
_exclude.add(e)
# determine the symbol and value of the next update (if any)
nextsymbval = self.calc(symb, e)
if nextsymbval is not None:
# there is another symbol to update, recursively call self.set
self.set(*nextsymbval, _exclude)
def calc(self, symb, e):
# find knowns and unknowns of the expression
known = [s for s in e.free_symbols if s in self.vals]
unknown = [s for s in e.free_symbols if s not in known]
if len(unknown) > 1:
# too many unknowns, can't do anything with this expression right now
return None
elif len(unknown) == 1:
# solve for the single unknown
nextsymb = unknown[0]
else:
# solve for the first known that isn't the symbol that was just changed
nextsymb = known[0] if known[0] != symb else known[1]
# do the actual solving
sol = solveset(e, nextsymb, domain=S.Reals)
# evaluate the solution given the known values, then return a tuple of (next-symbol, result)
return nextsymb, sol.subs(self.vals).args[0]
def __str__(self):
return ' '.join(sorted('{} = {}'.format(k,v) for k,v in self.vals.items()))
Testing it out:
mycalc = MyCalculator()
mycalc.set("a", 5)
mycalc.set("e", 7)
mycalc.set("c", 2)
print(mycalc)
Output:
a = 5 b = 2/5 c = 2 d = 33/5 e = 7 f = 2/7
One of the neat things about Sympy is that it uses rational math, which avoids any weird rounding errors in, for example, 2/7. If you'd prefer to get your results as standard Python float values, you can pass the usefloat flag to MyCalculator:
mycalc = MyCalculator(usefloat=True)
mycalc.set("a", 5)
mycalc.set("e", 7)
mycalc.set("c", 2)
print(mycalc)
Output:
a = 5.0 b = 0.4 c = 2.0 d = 6.6 e = 7.0 f = 0.2857142857142857
In [107]: a=2.
In [108]: a=5.
In [109]: b=0.4
In [110]: c=lambda: a*b
In [111]: d=6.6
In [112]: e=lambda: b+d
In [113]: f=lambda: c()/e()
In [114]: print(a,b,c(), d, e(), f())
5.0 0.4 2.0 6.6 7.0 0.2857142857142857
You can probably capture the above logic in a class.
It would be possible to hold 'variables' as _a, _b and _d. Then a(), b() and d() could be functions that return _a etc...
More a pointer than a whole answer but it may help.
Using a structure like that below it would be possible to create a situation where you always call a function and not need to know when to use a and c() but always use a() and c().
In [121]: def var(init=0.0):
...: def func(v=None):
...: nonlocal init
...: if v==None: return init
...: init=v
...: return func
...:
In [122]: a=var(100.)
In [123]: a()
Out[123]: 100.0
In [124]: a(25.)
In [125]: a()
Out[125]: 25.0
I'm getting a getting a TypeError for unbound method (at the bottom). I'm teaching myself Python so this may be some simple mistake. The issue is with outFormat(), which didn't give me problems when I test it by itself but is not working within the class. Here's the class:
class gf2poly:
#binary arithemtic on polynomials
def __init__(self,expr):
self.expr = expr
def id(self):
return [self.expr[i]%2 for i in range(len(self.expr))]
def listToInt(self):
result = gf2poly.id(self)
return int(''.join(map(str,result)))
def prepBinary(a,b):
a = gf2poly.listToInt(a); b = gf2poly.listToInt(b)
bina = int(str(a),2); binb = int(str(b),2)
a = min(bina,binb); b = max(bina,binb);
return a,b
def outFormat(raw):
raw = str(raw); g = []
[g.append(i) for i,c in enumerate(raw) if c == '1']
processed = "x**"+' + x**'.join(map(str, g[::-1]))
#print "processed ",processed
return processed
def divide(a,b): #a,b are lists like (1,0,1,0,0,1,....)
a,b = gf2poly.prepBinary(a,b)
bitsa = "{0:b}".format(a); bitsb = "{0:b}".format(b)
difflen = len(str(bitsb)) - len(str(bitsa))
c = a<<difflen; q=0
while difflen >= 0 and b != 0:
q+=1<<difflen; b = b^c
lendif = abs(len(str(bin(b))) - len(str(bin(c))))
c = c>>lendif; difflen -= lendif
r = "{0:b}".format(b); q = "{0:b}".format(q)
#print "r,q ",type(r),type(q)
return r,q #returns r remainder and q quotient in gf2 division
def remainder(a,b): #separate function for clarity when calling
r = gf2poly.divide(a,b)[0]; r = int(str(r),2)
return "{0:b}".format(r)
def quotient(a,b): #separate function for clarity when calling
q = gf2poly.divide(a,b)[1]; q = int(str(q),2)
return "{0:b}".format(q)
This is how I'm calling it:
testp = gf2poly.quotient(f4,f2)
testr = gf2poly.remainder(f4,f2)
print "quotient: ",testp
print "remainder: ",testr
print "***********************************"
print "types ",type(testp),type(testr),testp,testr
testp = str(testp)
print "outFormat testp: ",gf2poly.outFormat(testp)
#print "outFormat testr: ",gf2poly.outFormat(testr)
This is the error:
TypeError: unbound method outFormat() must be called with gf2poly instance as first argument (got str instance instead)
Where you have this:
def outFormat(raw):
You probably want either this:
def outFormat(self, raw):
Or this:
#staticmethod
def outFormat(raw):
The former if you eventually need access to self in outFormat(), or the latter if you do not (as currently is the case in the posted code).