很多答案都没有完整信息的一部分,因此我想将所有信息与我最喜欢的模式结合在一起。
默认值是一种mutable
类型
如果默认值是可变对象,那么您很幸运:您可以利用以下事实:在定义函数时,Python的默认参数将被评估一次(上一节答案末尾的更多信息)
这意味着您可以轻松地使用is
来比较默认的可变值,以查看它是作为参数传递还是默认保留,如以下示例中作为函数或方法所示:
def f(value={}):
if value is f.__defaults__[0]:
print('default')
else:
print('passed in the call')
和
class A:
def f(self, value={}):
if value is self.f.__defaults__[0]:
print('default')
else:
print('passed in the call')
不可变的默认参数
现在,如果您的默认值应该是一个immutable
值(记住,即使字符串是不可变的!),也会有点不太优雅,因为您无法按原样利用该技巧,但是仍然可以做些事情,仍然可以利用可变的类型; 基本上,您可以在函数签名中放置一个可变的“假”默认值,并在函数主体中放置所需的“真实”默认值。
def f(value={}):
"""
my function
:param value: value for my function; default is 1
"""
if value is f.__defaults__[0]:
print('default')
value = 1
else:
print('passed in the call')
print(value)
如果您的实际默认值为None
,那么它会特别有趣,但是它None
是不可变的,因此...您仍然需要显式地使用mutable作为函数的默认参数,并在代码中切换为None。
将Default
类用于不可变默认值
或者类似于@cz建议,如果python docs还不够:-),则可以在两者之间添加一个对象以使API更明确(无需阅读docs);或者 used_proxy_默认类实例是可变的,并将包含您要使用的实际默认值。
class Default:
def __repr__(self):
return "Default Value: {} ({})".format(self.value, type(self.value))
def __init__(self, value):
self.value = value
def f(default=Default(1)):
if default is f.__defaults__[0]:
print('default')
print(default)
default = default.value
else:
print('passed in the call')
print("argument is: {}".format(default))
现在:
>>> f()
default
Default Value: 1 (<class 'int'>)
argument is: 1
>>> f(2)
passed in the call
argument is: 2
上面的也很好用Default(None)
。
其他图案
显然,上述模式看起来比它们应有的丑陋,因为所有print
这些仅用于显示它们如何工作。否则,我会发现它们简洁而可重复。
您可以编写一个装饰器__call__
,以更简化的方式添加@dmg建议的模式,但这仍然必须在函数定义本身中使用怪异的技巧-您将需要拆分,value
并且value_default
如果您的代码需要区分它们,那么我没有看到太多优势,因此我不会写示例:-)
可变类型作为Python中的默认值
有关#1 python陷阱的更多信息!,出于您自己的喜好而受到滥用。您可以通过执行以下操作来查看定义时的评估结果:
def testme(default=[]):
print(id(default))
您可以根据需要运行任意testme()
多次,您将始终看到对同一默认实例的引用(因此,您的默认实例基本上是不可变的:-))。
请记住,在Python中只有3个可变内置类型:set
,list
,dict
,其他一切-甚至弦乐!-是一成不变的。