If else - if the code looks ugly by any clean solution?
I have about 20 functions (is_func1, is_fucn2, is_func3 ...) returning boolean
I am assuming there is only one function that returns true and I want that!
I am doing:
if is_func1(param1, param2):
# I pass 1 to following
abc(1) # I pass 1
some_list.append(1)
elif is_func2(param1, param2):
# I pass 2 to following
abc(2) # I pass 1
some_list.append(2)
...
.
.
elif is_func20(param1, param2):
...
Note: param1 and param2 are different for each, abc and some_list take parameters depending on the function.
The code looks great and there is repetition when calling abc and some_list, I can pull this login in the function! but is there any other cleanup solution?
I can think of putting the functions in a data structure and a loop to call them.
a source to share
I can think of putting the functions in a data structure and a loop to call them.
Yes, you probably should, as your code needs to be refactored
and data-centric design is a good choice.
An example similar to BlueRaja's answer ,
# arg1, arg2 and ret can have any values on each record
data = ((isfunc1, arg1, arg2, ret),
(isfunc2, arg1, arg2, ret),
(isfunc3, arg1, arg2, ret),
...)
for d in data:
if d[0](d[1], d[2]):
abc(d[3])
some_list.append(d[3])
break
a source to share
Try the following:
value = 1 if is_func1(param1, param2) else \
2 if is_func2(param1x, param2x) else \
... else \
20 if is_func20(param1z, param2z) else 0
abc(value)
some_list.append(value)
Keep in mind that this statement was floated together using various websites as a reference for the Python syntax, so please don't shoot me if it doesn't compile.
The main point is to create one value corresponding to a function called (1 for is_func1
, 2 for is_func2
, etc.), then use that value in the functions abc
and some_list.append
. Based on what I've been able to read about Python expression boolean, this should properly short-circuit the evaluation so that functions will stop being called as soon as one evaluates to true.
a source to share
This is a good use case for Chain of Responsibility .
I know how to give an example to objects, not functions, so I'll do this:
class HandleWithFunc1
def __init__(self, otherHandler):
self.otherHandler = otherHandler
def Handle(param1, param2):
if ( should I handle with func1? ):
#Handle with func1
return
if otherHandler == None:
raise "Nobody handled the call!"
otherHandler.Handle(param1, param2)
class HandleWithFunc2:
def __init__(self, otherHandler):
self.otherHandler = otherHandler
def Handle(param1, param2):
if ( should I handle with func1? ):
#Handle with func1
return
if otherHandler == None:
raise "Nobody handled the call!"
otherHandler.Handle(param1, param2)
So, you create all your classes as a chain:
handle = HandleWithFunc1(HandleWithFunc2())
then
handle.Handle(param1, param2)
This code is subject to refactoring, here only for illustration of use
a source to share
I modified BlueRaja's answer for different parameters ...
function_list = {is_func01: (pa1, pa2, ...),
is_func02: (pa1, pa2, pa3, ...),
....
is_func20: (pa1, ...)}
for func, pa_list in function_list.items:
if(func(*pa_list)):
abc(pa_list_dependent_parameters)
some_list.append(pa_list_dependent_parameters)
break
I don't understand why this shouldn't work.
a source to share
I haven't used python before, but can you reference functions by variable?
If so, you can create an enum with entries representing each function, check all the functions in the loop, and set the variable in the function enum to "true".
Then you can make a switch statement to enumerate.
However, this doesn't clean up the code much: when you have n options and need to collapse to the desired one, you need n blocks of code to process it.
a source to share
I'm not sure if this would be cleaner, but I think it's a pretty interesting solution.
First of all, you must define a new function, let it be semi_func
, that will call abc
and some_list.append
make the code DRY.
Then set a new variable to act as the binary of all boolean functions, so is_func1 is the 20th bit, is_func2 is 19, and so on. 32 bits of integer type should be sufficient to process all 20 results.
When setting the value of this result variable, you must use shift left to add new functions:
result = is_func1(param1, param2) << 1 result = (result | is_func2(param1, param2)) << 1 ... result = (result | is_func20(param1, param2))
For easier access, define new constants such as
IS_FUNC20_TRUE = 1 IS_FUNC19_TRUE = 2 IS_FUNC18_TRUE = 4 ... values should be powers of 2
And at the end, use a switch / sase statement to call semi_func
.
a source to share
I know that I will be cheated for being offtopic, but still. If you find something that can be done with standard control constructs, then you need to use another language, such as Common Lisp, which allows macros, essentially lets you create your own control constructors. (Having recently discovered anaphoric macros, I just have to recommend it.)
This particular case would be a perfect example where a macro could help, but only assuming you are doing this in multiple places in your code, otherwise it probably shouldn't be improved at all. And in fact, Common Lisp already has such a macro, it's called cond .
Anyway, in Python, I think you should go with a list of functions and a loop.
a source to share