Reputation: 3795
Let's say I have a method with a few optional parameters.
def foo(a, b=1, c=2, d=3)
How do I go about calling it so that if my variables are None or empty strings the defaults are used?
Conditionals like the following seems like a horrible solution:
if b and not c and d:
foo(myA, b = myB, d = myD)
elif b and not c and not d:
...
In Java I'd jump for a factory, but it seems like that's what defaults are supposed to avoid in this case.
Upvotes: 3
Views: 181
Reputation: 361730
I would change foo
so it replaces empty values with default ones.
def foo(a, b=None, c=None, d=None):
if not b: b = 1
if not c: c = 2
if not d: d = 3
Note that this will treat all "false-y" values as defaults, meaning not only None
and ''
but also 0
, False
, []
, etc. Personally I would tighten the interface up and use None
and only None
as a default value.
def foo(a, b=None, c=None, d=None):
if b is None: b = 1
if c is None: c = 2
if d is None: d = 3
Upvotes: 7
Reputation: 142176
Not fully tested, but should act as a base:
import inspect
from functools import wraps
def force_default(f):
@wraps(f)
def func(*args, **kwargs):
ca = inspect.getcallargs(f, *args, **kwargs)
da = inspect.getargspec(f)
dv = dict(zip(reversed(da.args), reversed(da.defaults)))
for k, v in ca.iteritems():
if v is None or v == '':
ca[k] = dv[k]
return f(**ca)
return func
@force_default
def foo(a, b=1, c=2, d=3):
print a, b, c, d
foo(6, '', None, 'rabbit!')
# 6 1 2 rabbit!
Upvotes: 1
Reputation: 6616
Though I agree that changing the method is a better idea, here's an alternative that changes the calling part by using a dict
of arguments, which is filtered and then unpacked:
d = {'b': myB, 'd': myD}
foo(myA, **{k: d[k] for k in d if d[k]})
Of course if d[k]
can be replaced by if d[k] not in {None, ''}
for example, which has a slightly different meaning (as pointed out by others).
Upvotes: 3
Reputation: 77347
You could call a function that filters out the variables you don't want passed down
def arg_filter(**kw):
return dict((k,v) for k,v in kw.items() if v not in (None, ''))
foo(**arg_filter(a=1,b=None,c=''))
Upvotes: 2
Reputation: 113985
If you want to catch ONLY None
and ''
:
def foo(a, b, c, d):
blacklist = set([None, ''])
if b in blacklist:
b = 1
if c in blacklist:
c = 2
if d in blacklist:
d = 3
If you want to catch all values v such that bool(v)
is False
, then:
def foo(a, b, c, d):
b = b or 1
c = c or 2
d = d or 3
Or you could decorate the function with another function that does the assertions for you (which may or may not be overkill, based on your use case)
Upvotes: 2