我偶然发现了一个我想解释的奇怪行为。为了说明这一点,请考虑以下功能(在Jupyter单元中编写):
def testfunc(res = [], a = {}, i = 0):
if i < 3:
b = dict(a) # make a local copy to keep in this recursion level
b[str(chr(97+i))] = i # fill some values
res.append(b) # push into result
res = testfunc(res,b,i+1) # recursion
return res
print(testfunc())
现在的输出符合预期:
[{'a':0},{'a':0,'b':1},{'a':0,'b':1,'c':2}]
如果我输出'res',则按预期,它不在范围内:
print(res)
NameError:名称'res'未定义
那么好。但是,现在,如果我在一个新的单元格中再次调用该函数:
print(testfunc())
发生这种情况:
[{'a':0},{'a':0,'b':1},{'a':0,'b':1,'c':2},{'a' :0},{'a':0,'b':1},{'a':0,'b':1,'c':2}]
这导致一个假设,即在函数末尾没有销毁res,也没有将空数组作为默认参数的精确值重新分配。 取而代之的是,包含前一个调用内容的参数被重用,我认为这是一种非常奇怪和意外的行为。奇怪的是,这只发生在对象(列表)上,而不是整数上,这可能是因为它是原始数据类型。
这应该是吗?当不带参数的情况下调用函数而使用以前的函数调用的变量时,为什么忽略默认参数的显式分配?这种行为有什么好处?
我了解可以通过转发这样的空列表来解决此问题
testfunc([])
,并且不使用默认参数,这使得默认参数基本上不仅无用,而且很危险,因为它们的用法似乎不可预测。还是我想念什么?
答案 0 :(得分:3)
这是因为res变量指向同一列表对象。 首次执行该命令时,列表已更改,并在其中添加了新元素。 在代码的第二次迭代中,添加了相同的列表。 请考虑以下示例:
ll
如果执行以下代码:
oo
将返回:
55
这是https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions的python文档中所述的
重要警告:默认值仅被评估一次。当默认值是可变对象(例如列表,字典或大多数类的实例)时,这会有所不同。例如,以下函数累积在后续调用中传递给它的参数: