The called function only gets the final argument value, whether that's the default value or something that's passed in. So what you need to do to be able to tell whether the caller passed in something is to make the default value something they can't pass in.
They can pass in None
, so you can't use that. What can you use?
The simplest solution is to create a Python object of some sort, use it as the default value in the function, and then del
it so users have no way to refer to it. Technically, users can dig this out of your function objects, but rarely will they go to such lengths, and if they do, you can argue that they deserve what they get.
default = []
def foo(a=default):
if a is default:
a = 10
print a
del default
The "default value" I'm using here is an empty list. Since lists are mutable, they're never reused, meaning that each empty list is a unique object. (Python actually uses the same empty tuple object for all empty tuples, so don't use an empty tuple! String literals and smallish integers are similarly reused, so don't use those either.) An object
instance would be fine. Anything as long as it's mutable and thus not to be shared.
Now the problem is the code above won't actually run since you've done del default
. How do you get a reference to this object in your function at run-time so you can test against it? One way to do it is with a default argument value which isn't used for an actual argument.
default = []
def foo(a=default, default=default):
if a is default:
a = 10
print a
del default
This binds the object to the argument's default value when the function is defined, so it doesn't matter that the global name is deleted a second later. But the problem there is if someone does help()
on your function, or otherwise introspects it, it will look like there is an extra argument that they can pass in. A better solution is a closure:
default = []
def foo(default=default): # binds default to outer func's local var default
def foo(a=default): # refers to outer func's local var default
if a is default:
a = 10
print a
return foo
foo = foo()
del default
This is all rather a lot of work to go to, so really, don't bother.