I am new to python and i have a quick question.
How can I avoid repeating my self when I declare the Class instances x1 x2 ..
I tried it with a list but then I wasn't able to create a file for each object after.
And not all parameters are the same for my objects, d[0] is counting up.
Any smart idea to get rid of repeating myself here?
thanks in advance
class TestClass(object):
def __init__(self, a, b, c: int):
self.a = a
self.b = b
self.c = c
def __str__(self):
return f" a= {self.a} b = {self.b} c = {self.c}"
def func1():
a = input("a: ")
b = input("b: ")
return a, b
def func2():
return 100, 90, 80, 70
c = func1()
d = func2()
x1 = TestClass(c[0], c[1], d[0])
x2 = TestClass(c[0], c[1], d[1])
x3 = TestClass(c[0], c[1], d[2])
x4 = TestClass(c[0], c[1], d[3])
h = {"a": x1,"b": x2, "c": x3, "d": x4}
for key, value in h.items():
with open(f"Name {key}.txt","w") as f:
f.write(str(value))
OUTPUT:
#a: Anton
#b: Bernd
#
# four files Name a - d.txt were created
# file 1: a= Anton b = Bernd c = 100
# file 2: a= Anton b = Bernd c = 90
# file 3: a= Anton b = Bernd c = 80
# file 4: a= Anton b = Bernd c = 70
You should iterate on the return value (tuple) of the func2 function (so on the d variable) with the enumerate function. The enumerate function returns the value and the related index of the iterator (Eg.: https://realpython.com/python-enumerate/). Then you can add the element for your (empty) dict. You should use the chr function to define the letters based on the index. The lowercase a is the 97.
Related code part:
c = func1()
d = func2()
h = {}
for idx, value in enumerate(d):
h[chr(97 + idx)] = TestClass(c[0], c[1], d[idx])
for key, value in h.items():
with open(f"Name {key}.txt", "w") as f:
f.write(str(value))
NOTE:
I have written a more compact version of code. You can check it if you are interested in it.
Code:
class TestClass(object):
def __init__(self, a, b, c: int):
self.a = a
self.b = b
self.c = c
def __str__(self):
return f" a= {self.a} b = {self.b} c = {self.c}"
a, b, h, d = input("a: "), input("b: "), {}, [100, 90, 80, 70]
result = [(chr(97 + idx), TestClass(a, b, d[idx])) for idx, value in enumerate(d)]
for item in result:
with open(f"Name {item[0]}.txt", "w") as f:
f.write(str(item[1]))
Quick answer
Use a Function, when you need to do something that's going to take you a lot of Typing or you need to do something repeatedly then pack it into a function.
def create_func(fun_1, fun_2):
result = {}
acii_n = 97
for item in fun_2:
name = chr(acii_n)
acii_n += 1
class_instance = TestClass(fun_1[0], fun_1[1], item)
result.setdefault(name, class_instance)
return result
h = create_func(c, d)
for key, value in h.items():
with open(f"Name {key}.txt","w") as f:
f.write(str(value))
chr(i) Function. You can see that I call the function starting at int 97.
That's because the ASCII value is the letter a --> asciitable.com.
Additional improvements
Funny enough the solution I gave, which is use a function, is also the exact opposite that I can suggest you to do for improve your script, which is remove the functions :).
class TestClass(object):
def __init__(self, a, b, c: int):
self.a = a
self.b = b
self.c = c
def __str__(self):
return f" a= {self.a} b = {self.b} c = {self.c}"
def create_instances(fun_2):
a = input("a: ")
b = input("b: ")
user_values = [a, b]
result = {}
ascii_n = 97
for item in fun_2:
name = chr(ascii_n)
ascii_n += 1 # Step on the next charactes
class_instance = TestClass(user_values[0], user_values[1], item)
result.setdefault(name, class_instance)
return result
int_values = [100, 90, 80, 70] # Just pack it into a list
all_instances = create_instances(int_values)
for key, value in all_instances.items():
with open(f"Name {key}.txt","w") as f:
f.write(str(value))
Using a Dictionary Comprehension
Very Powerful Tool, fast (can run Faster the For loops) and super Pythonic :) Python Dictionary Comprehension.
class TestClass(object):
def __init__(self, a, b, c: int):
self.a = a
self.b = b
self.c = c
def __str__(self):
return f" a= {self.a} b = {self.b} c = {self.c}"
int_values = [100, 90, 80, 70]
a = 'Python'
b = 'WOOW'
user_values = [a, b]
ascii_n = 97
result = {chr(ascii_n+idx): TestClass(user_values[0], user_values[1], item) for idx, item in enumerate(int_values)}
for key, value in result.items():
with open(f"Name {key}.txt","w") as f:
f.write(str(value))
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
Both functions do the same thing.
def function1(self):
a = self.get_a()
b = self.get_b()
c = self.get_c()
r = None
if a:
r = a
if b:
r = b
if c:
r = c
else:
print("c not set.")
else:
print("b not set.")
else:
print("a not set.")
return r
def function2(self):
a = self.get_a()
b = self.get_b()
c = self.get_c()
r = None
if not a:
print("a not set.")
return r
r = a
if not b:
print("b not set.")
return r
r = b
if not c:
print("c not set.")
r = c
return r
function1() creates very long lines the more if's are nested which conflicts with PEP8's line-length limit of 78.
function2() might be harder to read/understand and has more return statements. Line length is no problem here.
Which one is more pythonic?
One of the principals of Pythonic code is "flat is better than nested". On this basis, I'll say function2() is objectively more Pythonic. This can be seen in PEP-20: The Zen of Python:
The Zen of Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
This can be seen by typing import this inside the Python interpreter.
As #Will's answer suggests, flat is better. However the code doesn't look very pretty anyways. How about a more compact type of code?
looking at these quotes from #Will's answer:
Readability counts.
Beautiful is better than ugly.
from collections import OrderedDict
def function3():
my_dictionary=OrderedDict()
my_dictionary['a'] = self.get_a()
my_dictionary['b'] = self.get_b()
my_dictionary['c'] = self.get_c()
# ...
r = None
for name in my_dictionary.keys():
value = my_dictionary[name]
if not value:
print("%s not set." % name)
return r
r = value
return r
Surely this can be improved even more
You can use the evaluation rules of the and and or operators, for example:
>>> None or 4 or None or 5
4
>>> 4 and 5
5
So you'd have something like:
def function3(self):
a = self.get_a()
b = self.get_b()
c = self.get_c()
return (a and b and c) or (a and b) or a or None
And I'd recommend factoring out you I/O from your logical code.
I suggest function_4 displayed below together with the questions (non-idetnically working!) functions and one of DomTomCat's answer:
#! /usr/bin/env python
from __future__ import print_function
from collections import OrderedDict # Only used in function_3
def function_4(self):
"""Iterate over call results in FIFO on False or if sequence
exhausted, return None or previous value if that evaluates to true."""
functors = (
self.get_a,
self.get_b,
self.get_c,
)
request_targets = (
'a',
'b',
'c',
)
response_value = None
for functor, request_target in zip(functors, request_targets):
current_response = functor()
if not current_response:
print(request_target, "not set.")
return response_value
else:
response_value = current_response
return response_value
class Foo(object):
"""Mock the thingy ..."""
def __init__(self, a, b, c):
self._a, self._b, self._c = a, b, c
def __repr__(self):
return (
"Foo(" + str(self._a) + ", " + str(self._b) + ", " +
str(self._c) + ")")
def get_a(self):
return self._a
def get_b(self):
return self._b
def get_c(self):
return self._c
def function_1(self):
a = self.get_a()
b = self.get_b()
c = self.get_c()
r = None
if a:
r = a
if b:
r = b
if c:
r = c
else:
print("c not set.")
else:
print("b not set.")
else:
print("a not set.")
return r
def function_2(self):
a = self.get_a()
b = self.get_b()
c = self.get_c()
r = None
if not a:
print("a not set.")
return r
r = a
if not b:
print("b not set.")
return r
r = b
if not c:
print("c not set.")
r = c
return r
def function_3(self):
my_dictionary = OrderedDict()
my_dictionary['a'] = self.get_a()
my_dictionary['b'] = self.get_b()
my_dictionary['c'] = self.get_c()
# ...
r = None
for name in my_dictionary.keys():
value = my_dictionary[name]
if not value:
print("%s not set." % name)
return r
r = value
def main():
""""Drive the investigation."""
fixtures = (
(1, 42, 3.1415),
(0, 42, 3.1415),
(1, 0, 3.1415),
(1, 42, 0),
)
functors = (
function_1,
function_2,
function_3,
function_4,
)
for fixture in fixtures:
foo = Foo(*fixture)
print("\nFixture:", foo)
for i, functor in enumerate(functors, start=1):
print("Functor[%d]:" % (i,))
print(functor(foo))
if __name__ == '__main__':
main()
On my machine the fixtures produce the following behaviour/output when being called:
Fixture: Foo(1, 42, 3.1415)
Functor[1]:
3.1415
Functor[2]:
3.1415
Functor[3]:
None
Functor[4]:
3.1415
Fixture: Foo(0, 42, 3.1415)
Functor[1]:
a not set.
None
Functor[2]:
a not set.
None
Functor[3]:
a not set.
None
Functor[4]:
a not set.
None
Fixture: Foo(1, 0, 3.1415)
Functor[1]:
b not set.
1
Functor[2]:
b not set.
1
Functor[3]:
b not set.
1
Functor[4]:
b not set.
1
Fixture: Foo(1, 42, 0)
Functor[1]:
c not set.
42
Functor[2]:
c not set.
0
Functor[3]:
c not set.
42
Functor[4]:
c not set.
42
[Finished in 0.0s]
Here is what I would do without removing the print statements
def function1(self):
a = self.get_a()
b = self.get_b()
c = self.get_c()
r = None
inputValues = [a, b, c]
setValues = [i for i in inputValues if i]
for index, value in inputValues:
if len(setValues) <= index or setValues[index] != value:
print(f'{value} is not set.')
else:
r = value
return r
The function2 looks good enough to go.