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 call?
-
12Because I want to check it in that function of course :) – Matthias Feb 07 '13 at 10:54
-
3Just use `None` as the default and check for that. If you really could set up this test, you'd also exclude any possibility for the user to explicitly pass the value that invokes the default behavior. – Michael J. Barber Feb 07 '13 at 11:05
-
3That can be done in a much more reusable and beautiful way than in answer you accepted, at least for CPython. See my answer below. – Ellioh Feb 07 '13 at 11:25
-
2@Volatility: it matters if you have two sets of defaults. Consider a recursive class: `Class My(): def __init__(self, _p=None, a=True, b=True, c=False)` User calls it with `x=My(b=False)`. A class method could call itself with `x=My(_p=self, c=True)` if functions could detect that b is not explicitly set and that unset variables are to be passed down from the top level. But if they can't, the recursive calls have to pass every variable explicitly: `x=My(a=self.a, b=self.b, c=True, d=self.d, ...)`. – Dave Mar 08 '16 at 13:19
-
@Dave but is that what the question is about? In my understanding, the question is asking how to differentiate `x=My()` and `x=My(a=True)`. Your scenario involves assigning optional parameters a value other than their default value. – Volatility Mar 09 '16 at 11:04
-
Well, just found [`dict.pop(key[, default])`](https://docs.python.org/2/library/stdtypes.html#dict.pop) quite weird: "If `default` is not given and `key` is not in the dictionary, a `KeyError` is raised". And that leads me to this question. – neuront Nov 04 '16 at 01:49
-
Just ran into this question in a real codebase. The decorator from @Ellioh seems like the best approach, although usually the right decision will be to change the design so you don't need to solve this problem. – Karol Oct 14 '19 at 21:19
-
@MichaelJ.Barber That doesn't work if `None` as a valid argument the user can pass with it's own defined behavior. – Stevoisiak Sep 20 '22 at 20:10
10 Answers
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.

- 152,476
- 27
- 293
- 366
-
11I've also seen several people use the Ellipsis builtin for places where this is needed and None is considered valid input. This is essentially the same as the first example. – GrandOpener Feb 07 '13 at 11:17
-
If you want to implement special behaviour if None was passed, but still need a way to test if the argument was given by the user, you can use the `Ellipsis` singleton as a default, which was explicitly designed to be used as a *skip this* value. `...` is an alias for `Ellipsis`, so users who want to use positional arguments can just call `your_function(p1, ..., p3)` which makes it obvious and nice to read. – Bachsau Apr 17 '19 at 17:11
-
`However this is overly verbose and makes your function more difficult to use as its documentation will not automatically include the param parameter.` This is actually untrue, as you can set the description of a function and of its parameters using the `inspect` module. It depends on your IDE whether it'll work or not. – EZLearner Sep 25 '19 at 13:14
Lot of answers have little pieces of the full info, so I'd like to bring it all together with my favourite pattern(s).
default value is a mutable
type
If the default value is a mutable object, you are lucky: you can exploit the fact that Python’s default arguments are evaluated once when the function is defined (some more about this at the end of the answer in the last section)
This means you can easily compare a default mutable value using is
to see if it was passed as an argument or left by default, as in the following examples as function or method:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
and
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
Immutable default arguments
Now, it's a bit less elegant if your default is expected to be an immutable
value (and remember that even strings are immutable!) because you can't exploit the trick as it is, but there is still something you can do, still exploiting mutable type; basically you put a mutable "fake" default in the function signature, and the desired "real" default value in the function body.
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
# whatever I want to do with the value
print(value)
It feels particularly funny if you real default is None
, but None
is immutable so... you still need to explicitly use a mutable as the function default parameter, and switch to None in the code.
Using a Default
class for immutable defaults
or, similar to @c-z suggestion, if python docs are not enough :-) , you can add an object in between to make the API more explicit (without reading the docs); the used_proxy_ Default class instance is mutable, and will contain the real default value you want to use.
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
now:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
The above works nicely also for Default(None)
.
Other patterns
Obviously the above patterns looks uglier than they should because of all the print
which are there only for showing how they work. Otherwise I find them terse and repeatable enough.
You could write a decorator to add the __call__
pattern suggested by @dmg in a more streamlined way, but this will still oblige to use weird tricks in the function definition itself - you would need to split out value
and value_default
if your code need to distinguish them, so I don't see much advantage and I won't write the example :-)
Mutable types as default values in Python
A bit more about #1 python gotcha!, abused for your own pleasure above. You can see what happens due to the evaluation at definition by doing:
def testme(default=[]):
print(id(default))
You can run testme()
as many time as you want, you will always see a reference to the same default instance (so basically your default is immutable :-) ).
Remember that in Python there are only 3 mutable built-in types: set
, list
, dict
; everything else - even strings! - is immutable.

- 18,083
- 13
- 64
- 79
-
The example you have in "Immutable default arguments" does not actually have an immutable default argument. If it did, it wouldn't work. – Karol Oct 14 '19 at 20:30
-
@Karol, care to elaborate? The default value in that example is `1`, which should be immutable... – Stefano Oct 23 '19 at 13:02
-
-
@Karol that's the signature, but the _desired_ default value is `1`; sorry if that's not clear in the explanation, but the whole point of that part of the response is to be able to have an immutable default (`1`). If you check the example, you will see it says: `print('default'); value = 1`, not `value={}` – Stefano Nov 06 '19 at 14:24
-
...and the last option is to use an object to avoid the potential confusion... but I wanted to explain the principles first. – Stefano Nov 06 '19 at 14:29
-
1Ha, I get it now, thanks. It's not easy to follow unless someone reads your text very carefully which might not happen that often on SO. Consider rewording. – Karol Nov 07 '19 at 16:02
-
-
1In "if default is f.__defaults__[0]:", you have to hard-code which default parameter number to use, which may be fragile if the function signature changes. An alternative is "if default in f.__defaults__:". Assuming you use a different Default instance for each arg, "in" should work just as well as "is". – Stephen Warren Nov 23 '20 at 18:34
-
this is so much more complicated, for no obvious benefit, than the common method `DEFAULT=object()` as answered by ecamur. That answer should be the accepted one. – some bits flipped May 21 '21 at 14:55
-
Does the Immutable default arguments method also work for mutable arguments? – Stevoisiak Sep 20 '22 at 20:05
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)

- 5,162
- 2
- 20
- 33
-
This code works only in python2. For python 3, see my answer below: https://stackoverflow.com/questions/14749328/python-how-to-check-whether-optional-function-parameter-is-set/58166804#58166804 – R. Yang Sep 30 '19 at 11:17
-
1This is pretty cool, but better to avoid the problem with better design in the first place, if possible. – Karol Oct 14 '19 at 21:20
-
@Karol, I agree. In most cases one should not need that if the design is reasonable. – Ellioh Oct 16 '19 at 08:45
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.

- 67
- 1
- 2
-
6Python objects are references, you can just use `object()` instead of `uuid4()` - it's still a unique _instance_, which is what `is` checks – c z Dec 08 '17 at 10:57
I've seen this pattern a few times (e.g. library unittest
, py-flags
, jinja
):
class Default:
def __repr__( self ):
return "DEFAULT"
DEFAULT = Default()
...or the equivalent one-liner...:
DEFAULT = type( 'Default', (), { '__repr__': lambda x: 'DEFAULT' } )()
Unlike DEFAULT = object()
, this assists type-checking and provides information when errors occur -- frequently either the string representation ("DEFAULT"
) or the class name ("Default"
) are used in error messages.

- 7,726
- 3
- 46
- 59
@Ellioh's answer works in python 2. In python 3, the following code should work:
import inspect
from functools import wraps
def explicit_checker(f):
varnames = inspect.getfullargspec(f)[0]
@wraps(f)
def wrapper(*a, **kw):
kw['explicit_params'] = set(list(varnames[:len(a)]) + list(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
This method can keep the argument names and default values (instead of **kwargs) with better readability.
I agree with Volatility's comment. But you could check in the following manner:
def function(arg1,...,**optional):
if 'optional_arg' in optional:
# user has set 'optional_arg'
else:
# user has not set 'optional_arg'
optional['optional_arg'] = optional_arg_default_value # set default

- 18,848
- 3
- 60
- 59
-
I believe an optional parameter is something like `def func(optional=value)` not `**kwargs` – Zaur Nasibov Feb 07 '13 at 11:03
-
That's something which is somewhat open to interpretation. What's the actual difference between an argument with a default value and a keyword argument? They are both expressed using the same syntax "keyword=value". – isedev Feb 07 '13 at 11:05
-
I disagree, because the purpose of the optional parameters and `**kwargs` is a bit different. P.S. no problem's about -1 :) And my -1 for you was accidental :) – Zaur Nasibov Feb 07 '13 at 11:08
You can check it from foo.__defaults__
and foo.__kwdefaults__
see a simple example bellow
def foo(a, b, c=123, d=456, *, e=789, f=100):
print(foo.__defaults__)
# (123, 456)
print(foo.__kwdefaults__)
# {'e': 789, 'f': 100}
print(a, b, c, d, e, f)
#and these variables are also accessible out of function body
print(foo.__defaults__)
# (123, 456)
print(foo.__kwdefaults__)
# {'e': 789, 'f': 100}
foo.__kwdefaults__['e'] = 100500
foo(1, 2)
#(123, 456)
#{'f': 100, 'e': 100500}
#1 2 123 456 100500 100
then by using operator =
and is
you can compare them
and for some cases code bellow is enough
For example, you need to avoid changing default value then you can check on equality and then copy if so
def update_and_show(data=Example):
if data is Example:
data = copy.deepcopy(data)
update_inplace(data) #some operation
print(data)
Also, it is quite convenient to use getcallargs
from inspect
as it returns real arguments with which function will be invoked. You pass a function and args and kwargs to it (inspect.getcallargs(func, /, *args, **kwds)
), it will return real method's arguments used for invocation, taking into consideration default values and other stuff. Have a look at an example below.
from inspect import getcallargs
# we have a function with such signature
def show_params(first, second, third=3):
pass
# if you wanted to invoke it with such params (you could get them from a decorator as example)
args = [1, 2, 5]
kwargs = {}
print(getcallargs(show_params, *args, **kwargs))
#{'first': 1, 'second': 2, 'third': 5}
# here we didn't specify value for d
args = [1, 2, 3, 4]
kwargs = {}
# ----------------------------------------------------------
# but d has default value =7
def show_params1(first, *second, d = 7):
pass
print(getcallargs(show_params1, *args, **kwargs))
# it will consider b to be equal to default value 7 as it is in real method invocation
# {'first': 1, 'second': (2, 3, 4), 'd': 7}
# ----------------------------------------------------------
args = [1]
kwargs = {"d": 4}
def show_params2(first, d=3):
pass
print(getcallargs(show_params2, *args, **kwargs))
#{'first': 1, 'd': 4}

- 3,923
- 3
- 25
- 43
This is a variation on stefano's answer, but i find a little more readable:
not_specified = {}
def foo(x=not_specified):
if x is not_specified:
print("not specified")
else:
print("specified")

- 2,326
- 2
- 14
- 12
-
I downvoted because while this works at runtime, it creates an issue with typing (as most other answers). – bfontaine Feb 13 '22 at 21:33
-
-
@KristjanJonasson mypy sees this function as `foo(x: dict = not_specified) -> None`. The dummy value used as a default gives its type to the argument. If your function has a parameterized type it doesn’t work: `foo(x: T = not_specified)`; "`Incompatible default for argument "x" (default has type "Dict[Any, Any]", argument has type "T")`". You can use `Union[T, dict]` but that complicates the code. – bfontaine Feb 14 '22 at 19:39
-
It really seems that adopting some version of Fortran's **present** function would improve Python... – Kristjan Jonasson Feb 15 '22 at 20:19
-
Note that `mypy` is a package, it is not Python. Note also that type hinting is an optional feature of the language. If you work for, or desire your own code to use and comply with `mypy`, then it matters if this causes an issue with typing. If not, then it does not matter. – hlongmore Feb 19 '23 at 09:39
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.
-
2You might want to include the behavior of `check_f('z')`, which is also, as you say, freakish. – Michael J. Barber Feb 07 '13 at 11:23
-
@MichaelJ.Barber Good point. You'll have to do some "magic" with *args as well. However, my point was that it is possible, but needing to now whether the default value is passed or not is a bad design. – dmg Feb 07 '13 at 11:57