Reputation: 978
I am trying to find a clean way to handle different, mutually-exclusive function inputs. The idea is that I have a function which returns 4 values (these values are linked through mathematical equations), and when you input one of the 4 values, it returns all the values.
Currently the function works like this:
#example relations are simply: b=1+a, c=0.5*a, d=sqrt(a)
def relations(v, vtype="a"):
if vtype=="a":
a = v
elif vtype=="b":
a = v - 1
elif vtype=="c":
a = 2 * v
elif vtype=="d":
a = v ** 2
b = 1 + a
c = 0.5 * a
d = a ** 0.5
return a,b,c,d
The user specifies what the input variable is by means of a string vtype, and it returns all the values. It is not possible for the user to input more than one different input value (would be redundant, because all the unknowns can be determined from one input value).
Is there a more clean and pythonic way to do this? Specifying the input variable with a string feels really dirty at the moment.
Thanks in advance!
Upvotes: 1
Views: 433
Reputation: 3817
You could use something like this:
def relations(a=None, b=None, c=None, d=None):
if a is not None:
pass
elif b is not None:
a = b - 1
elif c is not None:
a = 2 * c
elif d is not None:
a = d ** 2
else:
raise TypeError('At least one argument needed')
# your calculations
Then you can use the function simply with e.g. relations(c=10)
or relations(a=2)
.
So you don't need the vtype
argument.
The first passed argument is used to calculate a
. If you call the function with more than one argument only the first will be used and the others will be ignored (e.g. relations(b=2, c=5)
only b
will be used and c
is ignored).
Upvotes: 0
Reputation: 102039
A common approach to avoid many if
-elif
s is to build a dictionary of functions:
def relations(v, vtype='a'):
functions = {
'a': lambda x: x, 'b': lambda x: x-1,
'c': lambda x: x * 2, 'd': lambda x: x**2
}
a = functions[vtype](v)
b = 1 + a
c = 0.5 * a
d = a ** 0.5
return a,b,c,d
If this function is not a bottleneck you can avoid using the lambda
s and simply do:
values = {'a': v, 'b': v-1, 'c': v * 2, 'd': v**2}
a = values[vtype]
If you don't like the idea of having vtype
in the function signature you can use a single **kwargs
argument:
def relations(**kwargs):
if len(kwargs) != 1 or not set('abcd').intersection(kwargs):
raise ValueError('Invalid parameters')
vtype, v = kwargs.popitem()
functions = {
'a': lambda x: x, 'b': lambda x: x-1,
'c': lambda x: x * 2, 'd': lambda x: x**2
}
a = functions[vtype](v)
b = 1 + a
c = 0.5 * a
d = a ** 0.5
return a,b,c,d
Then call it as:
relations(a=...)
relations(b=...)
Upvotes: 2
Reputation: 6661
You can define your vtypes as functions, and pass the function as parameter
def a(v):
return v
def b(v):
return v-1
def c(v):
return 2*v
def d(v):
return v**2
def relations(v, vtype=a):
value_a = vtype(v)
value_b = 1 + value_a
value_c = 0.5 * value_a
value_d = value_a ** 0.5
return value_a,value_b,value_c,value_d
With that you can get rid of the if
/elif
too.
Upvotes: 1
Reputation: 41873
You could use variable keyword arguments:
def relations(**kwargs):
if 'a' in kwargs:
a = kwargs['a']
elif 'b' in kwargs:
a = kwargs['b'] - 1
elif 'c' in kwargs:
a = kwargs['c'] * 2
elif 'd' in kwargs:
a = kwargs['d'] ** 2
else:
raise TypeError('missing an argument')
b = 1 + a
c = 0.5 * a
d = a ** 0.5
return a, b, c, d
Then use with named parameters:
relations(a=2)
relations(b=4)
relations(c=9)
relations(d=0)
Upvotes: 2