我正在寻找类似于 Lisp 的 arg-supplied-p 变量的东西,以帮助区分默认值和用户指定的相同值。
例子:
def foo(a=10):
pass
我想在 foo 中知道它是否被这样调用:
foo()
或像这样:
foo(10)
甚至像这样:
foo(a=10)
最后两个对我来说是同义词;我不需要知道那个细节。我稍微浏览了检查模块,但 getargspec 为所有 3 个调用返回完全相同的结果。
除了检查源代码之外,没有办法区分这三个函数调用——它们应该具有完全相同的含义。如果您需要区分它们,请使用不同的默认值,例如None
.
def foo(a=None):
if a is None:
a = 10
# no value for a provided
正如 Sven 所指出的,通常使用 None 作为默认参数。如果您甚至需要区分无参数和提供的显式 None 之间的区别,您可以使用 sentinel 对象:
sentinel = object()
def foo(a=sentinel):
if a is sentinel:
# No argument was provided
...
并不真地。您可以使用foo.func_defaults
来获取函数的默认参数列表,因此您可以使用它来检查传递参数的对象身份。但这只会对可变对象可靠地工作。就像您的示例一样,无法判断某人是通过了 10 还是使用了默认的 10,因为这两个 10 很可能是同一个对象。
无论如何,这只是一个粗略的近似,因为它没有告诉你函数是如何被调用的:它告诉你默认参数是什么,你查看实际参数,并尝试猜测它们是否相同。无法访问用于调用该函数的句法形式。
这是一个示例,它显示了它有时如何工作,有时又如何不工作。
>>> 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
被调用的函数只获取最终的参数值,无论是默认值还是传入的东西。所以你需要做的是能够判断调用者是否传入了一些东西是使默认值成为他们无法传递的东西在。
他们可以通过None
,所以你不能使用它。你能用什么?
最简单的解决方案是创建某种 Python 对象,将其用作函数中的默认值,然后del
用户无法引用它。从技术上讲,用户可以从你的函数对象中挖掘出来,但他们很少会这样做,如果他们这样做,你可以争辩说他们应得的。
default = []
def foo(a=default):
if a is default:
a = 10
print a
del default
我在这里使用的“默认值”是一个空列表。由于列表是可变的,它们永远不会被重用,这意味着每个空列表都是一个唯一的对象。(Python 实际上对所有空元组使用相同的空元组对象,所以不要使用空元组!字符串文字和较小的整数同样可以重用,所以也不要使用它们。)一个object
实例就可以了。任何东西,只要它是可变的,因此不能共享。
现在的问题是上面的代码实际上不会运行,因为你已经完成了del default
。如何在运行时在函数中获取对该对象的引用,以便对其进行测试?一种方法是使用不用于实际参数的默认参数值。
default = []
def foo(a=default, default=default):
if a is default:
a = 10
print a
del default
这将在定义函数时将对象绑定到参数的默认值,因此全局名称在一秒钟后被删除并不重要。但问题是,如果有人help()
对您的函数进行了处理,或者以其他方式对其进行了反省,看起来他们可以传入一个额外的参数。更好的解决方案是闭包:
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
这都是相当多的工作要做,所以真的,不要打扰。