返回后不会释放功能参数,也不会被默认参数替换。意外的行为?

时间:2019-04-16 12:46:59

标签: python jupyter-notebook

我偶然发现了一个我想解释的奇怪行为。为了说明这一点,请考虑以下功能(在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([])

,并且不使用默认参数,这使得默认参数基本上不仅无用,而且很危险,因为它们的用法似乎不可预测。还是我想念什么?

1 个答案:

答案 0 :(得分:3)

这是因为res变量指向同一列表对象。 首次执行该命令时,列表已更改,并在其中添加了新元素。 在代码的第二次迭代中,添加了相同的列表。 请考虑以下示例:

ll

如果执行以下代码:

oo

将返回:

55

这是https://docs.python.org/3/tutorial/controlflow.html#more-on-defining-functions的python文档中所述的

重要警告:默认值仅被评估一次。当默认值是可变对象(例如列表,字典或大多数类的实例)时,这会有所不同。例如,以下函数累积在后续调用中传递给它的参数: