我正在寻找像 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
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
这需要做很多工作,所以真的,不用费心。
None
默认与用户指定。自 2021 年起,PEP 661
告诉我们使用 Sentinel
类:
from sentinels import Sentinel
NotGiven = Sentinel('NotGiven')
def foo(a=NotGiven):
if a is NotGiven:
# No argument was provided
...