问题
I'm looking for something that works like Lisp's arg-supplied-p variables, to help differentiate a default value from the same value specified by a user.
Example:
def foo(a=10):
pass
I'd like to know in foo if it was called like this:
foo()
or like this:
foo(10)
or even like this:
foo(a=10)
The last 2 are synonymous enough to me; I don't need to know that detail. I've looked through the inspect module a bit, but getargspec returns exactly the same results for all 3 calls.
回答1:
Except for inspecting the source code, there is no way to tell the three function calls apart – they are meant to have exactly the same meaning. If you need to differentiate between them, use a different default value, e.g. None
.
def foo(a=None):
if a is None:
a = 10
# no value for a provided
回答2:
As Sven points out, it's typical to use None for a default argument. If you even need to tell the difference between no argument, and an explicit None being provided, you can use a sentinel object:
sentinel = object()
def foo(a=sentinel):
if a is sentinel:
# No argument was provided
...
回答3:
Not really. You can use foo.func_defaults
to get a list of the function's default arguments, so you could use that to check for object identity with the passed arguments. But this will only work reliably for mutable objects. There's no way to tell, as in your example, whether someone passed 10 or used the default 10, because both 10s are likely to be the same object.
This is only a rough approximation anyway, because it doesn't tell you how the function was called: it tells you what the default arguments are, and you look at the actual arguments, and try to guess whether they are the same. There is no way to get access to the syntactic form used to call the function.
Here's an example that shows how it sometimes works and sometimes doesn't.
>>> def f(x="this is crazy"):
... print x is f.func_defaults[0]
>>> f()
True
>>> f("this is crazy")
False
>>> def f(x=10):
... print x is f.func_defaults[0]
>>> f()
True
>>> f(10)
True
回答4:
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.
来源:https://stackoverflow.com/questions/11251119/is-there-any-way-to-know-if-the-value-of-an-argument-is-the-default-vs-user-spe