Pythonic solution for conditional arguments passing
Question:
I have a function with two optional parameters:
def func(a=0, b=10):
return a+b
Somewhere else in my code I am doing some conditional argument passing like:
if a and b:
return func(a, b)
elif a:
return func(a)
elif b:
return func(b=b)
else:
return func()
Is there anyway to simplify code in this pattern?
EDIT:
Let’s assume that I’m not allowed to implement default parameter logic inside func
.
I may have several functions like func
: func1
, func2
and func3
would all contain the
a = a or 0
b = b or 10
statements.
But I’m invoking these series of functions to eliminate duplication. (using a decorator)
Answers:
Why not pass that logic to the function?
def func(a, b):
a = a or 0
b = b or 10
return a + b
to solve your specific question I would do:
args = {'a' : a, 'b' : b}
for varName, varVal in args.items():
if not varVal:
del args[varName]
f(**args)
But the most pythonic way would be to use None as the default value in your function:
f(a=None, b=None):
a = 10 if a is None else a
...
and just call f(a, b)
Going by the now-deleted comments to the question that the check is meant to be for the variables being None
rather than being falsey, change func
so that it handles the arguments being None
:
def func(a=None, b=None):
if a is None:
a = 0
if b is None:
b = 10
And then just call func(a, b)
every time.
By default, all methods in Python take variable arguments.
When you define an argument in the signature of the method, you explicity make it required. In your snippet, what you are doing is giving it a default value – not making them optional.
Consider the following:
>>> def func(a,b):
... a = a if a else 0
... b = b if b else 10
... return a+b
...
>>> a = b = None
>>> func(a,b)
10
>>> a = 5
>>> b = 2
>>> func(a,b)
7
>>> func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 2 arguments (0 given)
In this snippet, both a
and b
are required, since I didn’t define any default values. They are not optional.
However in your snippet, because you have given them defaults in the method signature they look like they are optional, but in fact they just have defaults assigned to them.
>>> def func(a=0,b=10):
... return a+b
...
>>> func()
10
You can confirm this by checking the argument list inside the body of the method:
>>> def func(a=b,b=10):
... print locals().keys()
...
>>> func()
['a', 'b']
One way to have your function accept any number of arguments (in other words, making them all optional):
>>> def func(*args,**kwargs):
... print len(args),args
... print len(kwargs),kwargs
...
>>> func("hello","world",a=5,b=10)
2 ('hello', 'world')
2 {'a': 5, 'b': 10}
>>> func()
0 ()
0 {}
>>> func(1,2)
2 (1, 2)
0 {}
>>> func(a)
1 (5,)
0 {}
>>> func(foo="hello")
0 ()
1 {'foo': 'hello'}
You can add a decorator that would eliminate None
arguments:
def skip_nones(fun):
def _(*args, **kwargs):
for a, v in zip(fun.__code__.co_varnames, args):
if v is not None:
kwargs[a] = v
return fun(**kwargs)
return _
@skip_nones
def func(a=10, b=20):
print a, b
func(None, None) # 10 20
func(11, None) # 11 20
func(None, 33) # 10 33
To answer your literal question:
func(a or 0, b or 10)
Edit:
See comment why this doesn’t answer the question.
This might work:
def f(**kwargs):
a = get(kwargs, 0)
b = get(kwargs, 10)
return a + b
If you don’t want to change anything in func
then the sensible option would be passing a dict of arguments to the function:
>>> def func(a=0,b=10):
... return a+b
...
>>> args = {'a':15,'b':15}
>>> func(**args)
30
>>> args={'a':15}
>>> func(**args)
25
>>> args={'b':6}
>>> func(**args)
6
>>> args = {}
>>> func(**args)
10
or just:
>>>func(**{'a':7})
17
You can use the ternary if-then operator to pass conditional arguements into functions
https://www.geeksforgeeks.org/ternary-operator-in-python/
For example, if you want to check if a key actually exists before passing it you can do something like:
def func(arg):
print(arg)
kwargs = {'name':'Adam')
func( kwargs['name'] if 'name' in kwargs.keys() else '' ) #Expected Output: 'Adam'
func( kwargs['notakey'] if 'notakey' in kwargs.keys() else '' ) #Expected Output: ''
I have a function with two optional parameters:
def func(a=0, b=10):
return a+b
Somewhere else in my code I am doing some conditional argument passing like:
if a and b:
return func(a, b)
elif a:
return func(a)
elif b:
return func(b=b)
else:
return func()
Is there anyway to simplify code in this pattern?
EDIT:
Let’s assume that I’m not allowed to implement default parameter logic inside func
.
I may have several functions like func
: func1
, func2
and func3
would all contain the
a = a or 0
b = b or 10
statements.
But I’m invoking these series of functions to eliminate duplication. (using a decorator)
Why not pass that logic to the function?
def func(a, b):
a = a or 0
b = b or 10
return a + b
to solve your specific question I would do:
args = {'a' : a, 'b' : b}
for varName, varVal in args.items():
if not varVal:
del args[varName]
f(**args)
But the most pythonic way would be to use None as the default value in your function:
f(a=None, b=None):
a = 10 if a is None else a
...
and just call f(a, b)
Going by the now-deleted comments to the question that the check is meant to be for the variables being None
rather than being falsey, change func
so that it handles the arguments being None
:
def func(a=None, b=None):
if a is None:
a = 0
if b is None:
b = 10
And then just call func(a, b)
every time.
By default, all methods in Python take variable arguments.
When you define an argument in the signature of the method, you explicity make it required. In your snippet, what you are doing is giving it a default value – not making them optional.
Consider the following:
>>> def func(a,b):
... a = a if a else 0
... b = b if b else 10
... return a+b
...
>>> a = b = None
>>> func(a,b)
10
>>> a = 5
>>> b = 2
>>> func(a,b)
7
>>> func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() takes exactly 2 arguments (0 given)
In this snippet, both a
and b
are required, since I didn’t define any default values. They are not optional.
However in your snippet, because you have given them defaults in the method signature they look like they are optional, but in fact they just have defaults assigned to them.
>>> def func(a=0,b=10):
... return a+b
...
>>> func()
10
You can confirm this by checking the argument list inside the body of the method:
>>> def func(a=b,b=10):
... print locals().keys()
...
>>> func()
['a', 'b']
One way to have your function accept any number of arguments (in other words, making them all optional):
>>> def func(*args,**kwargs):
... print len(args),args
... print len(kwargs),kwargs
...
>>> func("hello","world",a=5,b=10)
2 ('hello', 'world')
2 {'a': 5, 'b': 10}
>>> func()
0 ()
0 {}
>>> func(1,2)
2 (1, 2)
0 {}
>>> func(a)
1 (5,)
0 {}
>>> func(foo="hello")
0 ()
1 {'foo': 'hello'}
You can add a decorator that would eliminate None
arguments:
def skip_nones(fun):
def _(*args, **kwargs):
for a, v in zip(fun.__code__.co_varnames, args):
if v is not None:
kwargs[a] = v
return fun(**kwargs)
return _
@skip_nones
def func(a=10, b=20):
print a, b
func(None, None) # 10 20
func(11, None) # 11 20
func(None, 33) # 10 33
To answer your literal question:
func(a or 0, b or 10)
Edit:
See comment why this doesn’t answer the question.
This might work:
def f(**kwargs):
a = get(kwargs, 0)
b = get(kwargs, 10)
return a + b
If you don’t want to change anything in func
then the sensible option would be passing a dict of arguments to the function:
>>> def func(a=0,b=10):
... return a+b
...
>>> args = {'a':15,'b':15}
>>> func(**args)
30
>>> args={'a':15}
>>> func(**args)
25
>>> args={'b':6}
>>> func(**args)
6
>>> args = {}
>>> func(**args)
10
or just:
>>>func(**{'a':7})
17
You can use the ternary if-then operator to pass conditional arguements into functions
https://www.geeksforgeeks.org/ternary-operator-in-python/
For example, if you want to check if a key actually exists before passing it you can do something like:
def func(arg):
print(arg)
kwargs = {'name':'Adam')
func( kwargs['name'] if 'name' in kwargs.keys() else '' ) #Expected Output: 'Adam'
func( kwargs['notakey'] if 'notakey' in kwargs.keys() else '' ) #Expected Output: ''