Is there an easy way in Python to check whether the value of an optional parameter comes from its default value, or because the user has set it explicitly at the function ca
Not really. The standard way is to use a default value that the user would not be expected to pass, e.g. an object
instance:
DEFAULT = object()
def foo(param=DEFAULT):
if param is DEFAULT:
...
Usually you can just use None
as the default value, if it doesn't make sense as a value the user would want to pass.
The alternative is to use kwargs
:
def foo(**kwargs):
if 'param' in kwargs:
param = kwargs['param']
else:
...
However this is overly verbose and makes your function more difficult to use as its documentation will not automatically include the param
parameter.
I sometimes use a universally unique string (like a UUID).
import uuid
DEFAULT = uuid.uuid4()
def foo(arg=DEFAULT):
if arg is DEFAULT:
# it was not passed in
else:
# it was passed in
This way, no user could even guess the default if they tried so I can be very confident that when I see that value for arg
, it was not passed in.
A little freakish approach would be:
class CheckerFunction(object):
def __init__(self, function, **defaults):
self.function = function
self.defaults = defaults
def __call__(self, **kwargs):
for key in self.defaults:
if(key in kwargs):
if(kwargs[key] == self.defaults[key]):
print 'passed default'
else:
print 'passed different'
else:
print 'not passed'
kwargs[key] = self.defaults[key]
return self.function(**kwargs)
def f(a):
print a
check_f = CheckerFunction(f, a='z')
check_f(a='z')
check_f(a='b')
check_f()
Which outputs:
passed default
z
passed different
b
not passed
z
Now this, as I mentioned, is quite freakish, but it does the job. However this is quite unreadable and similarly to ecatmur's suggestion won't be automatically documented.
The following function decorator, explicit_checker
, makes a set of parameter names of all the parameters given explicitly. It adds the result as an extra parameter (explicit_params
) to the function. Just do 'a' in explicit_params
to check if parameter a
is given explicitly.
def explicit_checker(f):
varnames = f.func_code.co_varnames
def wrapper(*a, **kw):
kw['explicit_params'] = set(list(varnames[:len(a)]) + kw.keys())
return f(*a, **kw)
return wrapper
@explicit_checker
def my_function(a, b=0, c=1, explicit_params=None):
print a, b, c, explicit_params
if 'b' in explicit_params:
pass # Do whatever you want
my_function(1)
my_function(1, 0)
my_function(1, c=1)