可能重复:
“Least Astonishment” in Python: The Mutable Default Argument
考虑以下两个功能
def a(var=[0]):
print (var)
var = var + [4]
def b(var=[0]):
print (var)
var.append(4)
它们的行为应该相同,但更重要的是,如果没有参数调用,两者都应该简单地打印'[0]'。行为完全不同,只有a()会一直打印'[0]'。
>>> a()
[0]
>>> a()
[0]
>>> a()
[0]
>>> b()
[0]
>>> b()
[0, 4]
>>> b()
[0, 4, 4]
为什么a()的功能与b()不同?
如果您希望在没有参数传递给函数的情况下使用默认值覆盖变量,则似乎无法使用列表API。如果你这样做,该功能将“记住”变量之前的内容。
我的代码中的情况出现在一个递归函数中,所以只是“删除”不需要的变量就不会真正起作用。每次调用没有参数的函数时,有没有办法覆盖变量?
经过数小时和数小时的研究,我发现了这一点。它可能与上述问题有关,可能会导致答案。我们可以像这样定义一个生命周期类:
class lifetime: # adapted from http://naml.us/blog/tag/variable-lifetime
def __init__(self, name):
self.name = name
print ('creating: ' + name)
def __del__(self):
print ('destroying: ' + self.name)
def __iadd__(self, a):
self.append(a)
def append(self, a):
self.name += ' and ' + a.name
print('appending: ' + a.name + ' to ' + self.name)
然后定义2个函数:
def a(var=lifetime('a')):
print (var)
var += life('appendage')
def b(var=lifetime('b')):
print (var)
var.append(life('appendage'))
>>> a()
<__main__.lifetime object at 0x00000000031FFA90>
creating: appendage
appending: appendage to a and appendage
destroying: appendage
>>> a()
<__main__.lifetime object at 0x00000000031FFA90>
creating: appendage
appending: appendage to a and appendage and appendage
destroying: appendage
>>> b()
<__main__.lifetime object at 0x00000000031FFB38>
creating: appendage
appending: appendage to b and appendage
destroying: appendage
>>> b()
<__main__.lifetime object at 0x00000000031FFB38>
creating: appendage
appending: appendage to b and appendage and appendage
destroying: appendage
它似乎只是评估参数的默认值一次,然后使用任何评估。它永远不会说'创造:'或'创造:b'。因此,如果每次都要评估默认参数,那么它将导致对实际问题的回答。
答案 0 :(得分:1)
正如你所说的那样,当声明函数时,这些默认值只评估一次。这就是为什么将列表或散列文字指定为默认值永远不是一个好主意,因为你只会为所有函数调用获得一个实例。
该问题的描述为here。
答案 1 :(得分:1)
这两个函数的行为有所不同,因为在第一个函数中,您将局部变量var
重新定义为[0] + [4]
(不附加到使用[0]
初始化的原始列表),而在第二个函数中您实际上是附加到原始初始化列表,以便将其从[0]
转移到[0, 4]
。正如您所提到的 - 默认值只评估一次。
答案 2 :(得分:1)
因为这两个代码片段做了两件固有不同的事情。 var + [4]
创建了一个新列表,其中包含添加了var
的{{1}}列表的内容,然后我们将此新列表分配给4
。另一方面,var
将数字4附加到现有列表中。
您的问题是var.append(4)
只被评估一次然后重复使用,因为您的var=[0]
更改了变量本身,您将看到对以后调用的影响。另一个代码只将一个新对象分配给一个稍后不会产生影响的局部变量。
通常你不使用可修改的默认值,而是写:
append
如果您需要记住早期调用中的数据,请将整个函数放入类中。
答案 3 :(得分:1)
如果您将其写成
,可能更容易想象正在发生的事情t1 = [0]
def a(var=t1):
print (var)
var = var + [4]
t2 = [0]
def b(var=t2):
print (var)
var.append(4)
答案 4 :(得分:0)
这很容易。在第一个函数a()
中,您将“名称”var
重新分配给包含旧var
和[4]
内容的新列表。在第二个函数b()
中,您正在访问实际的var
对象,而不更改名称所指的内容。请参阅此问题,了解b()
为何回应原因的解释:“Least Astonishment” in Python: The Mutable Default Argument