I have a function which is designed to be called by passing in one of to keyword arguments. I'm using a sentinel object as default value, so that I can make sure no one just calls func()
without any arguments, which is a clear logical error. It is ok to call the function by passing None
as a value for one of the arguments, in those cases it just doesn't do any processing.
NO_VALUE = object()
def func(*, arg1 = NO_VALUE, arg2 = NO_VALUE):
if arg1 is NO_VALUE and arg2 is NO_VALUE:
raise ValueError("Pass in one of `arg1` or arg2`.")
if arg1 is not NO_VALUE and arg1:
# Do something with a truthy `arg1` value.
if arg2 is not NO_VALUE and arg2:
# Do something with a truthy `arg2` value.
Could I somehow easily make NO_VALUE
be falsy, so that I could simplify the if arg1 is not NO_VALUE and arg1
and if arg2 is not NO_VALUE and arg2
to just if arg1
and if arg2
respectively?
I tried making NO_VALUE
an empty tuple ()
but it seems that the id()
of an empty tuple is always(?) same as the id()
of any other empty tuple. I also don't want to make NO_VALUE
e.g. an empty list object, since then I'd get linter warnings about using a mutable default value.
In any case: for a Python object to be "Falsy" it can be an instance of a class which implements __bool__
and returns False
when that is called:
class FalsySentinel:
def __bool__(self):
return False
NO_VALUE = FalsySentinel()
def func(*, arg1 = NO_VALUE, arg2 = NO_VALUE):
if arg1 is NO_VALUE and arg2 is NO_VALUE:
raise ValueError("Pass in one of `arg1` or arg2`.")
if arg1:
# Do something with a truthy `arg1` value.
if arg2:
# Do something with a truthy `arg2` value.
There are other ways to produce falsy objects, like objects that implement __len__
and return 0
on it - but this is the most explicit and straightforward way.