Reputation: 6621
I have a function that checks 3 different boolean flags and has a unique outcome for each of the 8 combinations.
"""
a | b | c | out
- + - + - + ---
0 | 0 | 0 | 0
0 | 0 | 1 | 1
0 | 1 | 0 | 2
0 | 1 | 1 | 3
1 | 0 | 0 | 4
1 | 0 | 1 | 5
1 | 1 | 0 | 6
1 | 1 | 1 | 7
"""
def do_stuff(a, b, c):
if a:
if b:
if c:
return function7() # a, b, c
return function6() # a, b, !c
if c:
return function5() # a, !b, c
return function4() # a, !b, !c
else:
if b:
if c:
return function3() # !a, b, c
return function2() # !a, b, !c
if c:
return function1() # !a, !b, c
return function0() # !a, !b, !c
I'm already short-cutting a lot of else
statements since I'm returning (exiting the loop).
Is there a more DRY way to accomplish this? I could convert to binary, and do a single depth level of if/elif, but I don't want to use "magic numbers".
Also, I realize this is only 16 lines of code for 8 outcomes, but what if it were 4 variables, is there a way to increase readability/flow?
Upvotes: 0
Views: 1317
Reputation: 482
First you can build a binary to decimal converter.After obtaining the new number you can call the method by function name in an string by eval function:
def function0():
print("function0")
def function1():
return 1;
a = eval("function1()")
print(a)
Remember that you can pass global variables to eval function unless the current global names id used.
Upvotes: -2
Reputation: 114230
You need to have some sort of mapping from outcome to function and a mapping from input to outcome. The nifty table you provide at the top, combined with the fact that bool
s in Python are just the integers 0
and 1
gives you the following:
outcomeToFunc {
0: function0,
1: function1,
2: function2,
3: function3,
4: function4
# etc...
}
def inputToOutcome(a, b, c):
return a << 2 | b << 1 | c
def do_stuff(a, b, c):
outcome = inputToOutcome(a, b, c)
return outcomeToFunc[outcome]()
If you are interested in having an arbitrary number of boolean inputs, you can modify do_stuff
and inputToOutcome
to use varargs:
outcomeToFunc {
0: function0,
1: function1,
2: function2,
3: function3,
4: function4
# etc...
}
def inputToOutcome(*args):
outcome = 0
n = len(args) - 1
for ind, arg in enumerate(args):
outcome |= bool(arg) << (n - ind)
return outcome
def do_stuff(*args):
outcome = inputToOutcome(*args)
try:
return outcomeToFunc[outcome]()
except KeyError:
raise ValueError('Outcome {} not supported'.format(outcome))
Upvotes: 4
Reputation: 168616
Here are two solutions. It is up to you to decide if they are prettier than the one you presented.
def do_stuff(a, b, c):
flags = bool(a), bool(b), bool(c)
if flags == (True, True, True):
return function7()
if flags == (True, True, False):
return function6()
if flags == (True, False, True):
return function5()
if flags == (True, False, False):
return function4()
if flags == (False, True, True):
return function3()
if flags == (False, True, False):
return function2()
if flags == (False, False, True):
return function1()
if flags == (False, False, False):
return function0()
def do_stuff(a, b, c):
control = {
(True, True, True): function7,
(True, True, False): function6,
(True, False, True): function5,
(True, False, False): function4,
(False, True, True): function3,
(False, True, False): function2,
(False, False, True): function1,
(False, False, False): function0,
}
return control[bool(a), bool(b), bool(c)]()
Upvotes: 1