I have a function to calculate an engineering parameter via a few different methods.
Is there a better approach than any of the below?
Input variables:
strength = 10
gradient = 2
depth = 5
factor = 9
alpha = 2.3
Approach 1
def func1(option, *args):
if option==1:
return args[0] args[1] * args[2]
elif option==2:
return args[0] * args[1]
Called with either:
func1(1, strength, gradient, depth)
func1(2, factor, alpha)
- Pros: Function call does not require duplicating names
- Cons: What function is doing is unclear unless you match up with the function call
Approach 2
def func2(option, **kwargs):
if option==1:
return kwargs['strength'] kwargs['gradient'] * kwargs['depth']
elif option==2:
return kwargs['factor'] * kwargs['alpha']
Called with either:
func2(option=1, strength=strength, gradient=gradient, depth=depth)
func2(option=2, factor=factor, alpha=alpha)
- Pros: What function is doing is clear from function alone, albeit a bit clunky
- Cons: Cumbersome function call as names are duplicated, especially for more complex functions
Approach 3
def func4(option, **kwargs):
if option==1:
return strength gradient * depth
elif option==2:
return factor * alpha
Called with either:
dict1 = {'strength': 10, 'gradient': 2, 'depth': 5, 'factor': 9, 'alpha': 2.3}
func3(1, **dict1)
func3(2, **dict1)
- Pros: What function is doing is clear from function alone. Call is simple & flexible.
- Cons: Must view the dictionary to be sure what variables the function call is passing.
CodePudding user response:
Ideally, you should try to make 2 distinct functions with self-descriptive names & call the function according to need. All your current iterations are opaque about how is first argument working and the next people reading this code will have to make logical connections themselves.
However, assuming that is not possible in your problem case, you should go with Approach 3
and use assert
to make sure dictionaries passed consists of the keys it needs (with appropriate value constraints as per need).
CodePudding user response:
I'd probably go with a variation on Approach 1. The way I've done this type of thing in the past is making a function map, with the keys being your options:
def func1(strength, gradient, depth):
return strength gradient * depth
def func2(factor, alpha):
return factor * alpha
func_map = {1: func1, 2: func2}
func_map[1](strength, gradient, depth)
func_map[2](factor, alpha)
It makes it explicit what the functions are doing, and makes unit testing easy. You could also throw in kwargs in there if you feel strongly about making it easy to call without knowing what arguments go into what function, although you lose some traceability in that approach:
def func1(strength, gradient, depth, **kwargs):
return strength gradient * depth
def func2(factor, alpha, **kwargs):
return factor * alpha
func_map = {1: func1, 2: func2}
args = {strength: 1, gradient: 2, depth: 3, factor: 4, alpha: 5}
func_map[1](**args)
func_map[2](**args)