仍然对Python中可变的默认参数值“gotcha”感到困惑

时间:2017-09-25 09:39:39

标签: python function default

我理解不应该在Python中使用mutable default parameter value(有一些例外),因为这个值在定义函数时只被评估和存储一次,而不是每次稍后调用该函数。

我对此的理解是这样的(使用下面的例子;请原谅我不精确的语言,因为我只是Python编程的初学者,因为这个而被困在我教科书的功能章节中):

def f(x = [1, 2, 3]):
    x.append(4)
    print(x)

f()
f()

1)定义函数f,x(f中的局部变量)假设默认变量为[1,2,3](甚至在调用函数之前)

2)当调用f()时,由于没有传递参数,x仍然是[1,2,3],并且x继续具有其默认值

3)x用append修改,变成[1,2,3,4],并按原样打印

然而,这是我的困惑所在。我假设:

4)当f结束时,x被破坏(在堆栈中或你称之为的任何东西)并且不再与列表对象[1,2,3,4]相关联**

5)回收列表对象[1,2,3,4],因为不再有引用它的变量

因此,

6)当第二次调用f()时,我希望Python输出一个错误,因为x现在不再有与之关联的值了。换句话说,当Python被回收/销毁时,Python如何重用上一次评估的默认值?

感谢您的所有帮助和解释!

**我从Ned Batchelder's page on variable name assignment得到的这种理解(见下文) Net Batchelder's variable assignment function

2 个答案:

答案 0 :(得分:7)

虽然你可能觉得在执行结束时x,默认值被处理掉,但事实并非如此。

事实上,Python有一个全局命名空间,其中包含所有可供您使用的名称(您导入或定义的内置函数,类和函数)。

此命名空间的内容由对象组成。功能也是对象。

作为测试,如果你在脚本或python命令行中尝试这个,你会看到我的意思:

def f(x = [1, 2, 3]):
    x.append(4)
    print(x)
print dir(f)

你会看到函数f的对象性质。作为对象,默认值在属性f.func_defaults中引用,因此它们始终可用,如果可变,它们会保留更改,为您提供可能不需要的副作用。

编辑:在python 3中,该属性已被f.__defaults__

取代

答案 1 :(得分:3)

在您的情况下,列表有两个引用,一个是在函数的后台存储,作为参数x的默认值。

在没有x的情况下调用函数时,会创建对相同列表的新引用作为局部变量x。然后通过第二个引用附加到列表。并且在调用之后,第二个引用被垃圾收集。第一个引用仍然指向同一个列表,现在有一个元素。

或者简而言之:一直只有一个列表。