我正在学习Python,我正在处理the Mutable Default Argument problem。
# BAD: if `a_list` is not passed in, the default will wrongly retain its contents between successive function calls
def bad_append(new_item, a_list=[]):
a_list.append(new_item)
return a_list
# GOOD: if `a_list` is not passed in, the default will always correctly be []
def good_append(new_item, a_list=None):
if a_list is None:
a_list = []
a_list.append(new_item)
return a_list
我了解a_list
仅在首次遇到def
语句时初始化,这就是bad_append
的后续调用使用相同列表对象的原因。
我不明白为什么good_append
有所不同。看起来a_list
仍只能初始化一次;因此,if
语句只会在第一次调用函数时成立,这意味着a_list
只会在第一次调用时重置为[]
,这意味着它仍然会累积所有过去的new_item
{1}}价值观仍然存在问题。
为什么不呢?我错过了什么概念?每次a_list
运行时good_append
如何清除干净?
答案 0 :(得分:22)
看起来a_list只会初始化一次
“初始化”不是Python中变量发生的事情,因为Python中的变量只是名称。 “初始化”只发生在对象上,它是通过类“__init__
方法完成的。
当你写a = 0
时,这是一项任务。这就是说“a
应该引用表达式0
”所描述的对象。它不是初始化; a
可以在以后任何时候命名任何类型的任何其他内容,并且这是因为将其他内容分配给a
而发生的。作业只是作业。第一个并不特别。
当您撰写def good_append(new_item, a_list=None)
时,这不是“初始化”a_list
。它正在设置对象的内部引用,即评估None
的结果,以便在没有第二个参数的情况下调用good_append
时,该对象会自动分配给a_list
。
意味着a_list只会在第一次调用时重置为[]
不,a_list
在[]
a_list
开始的任何时候都设置为None
。也就是说,当None
显式传递,或者省略参数时。
[]
出现问题是因为在此上下文中表达式 []
仅评估一次。编译函数时,会计算[]
,创建一个特定的列表对象 - 启动时恰好是空的 - 并且该对象用作默认值。
每次
a_list
运行时good_append
如何清除?
没有。它不需要。
您知道问题是如何被描述为使用“可变默认参数”吗?
None
不可变。
修改参数作为默认值的对象时会出现问题。
a_list = []
不会修改之前引用的任何对象a_list
。这不可以;任意对象不能神奇地就地转换为空列表。 a_list = []
表示“a_list
将停止引用之前提到的内容,并开始引用[]
”。之前提到的对象保持不变。
当编译函数并且其中一个参数具有默认值时,该值 - 对象 - 将被烘焙到函数中(它本身也是一个对象!)。当您编写改变对象的代码时,该对象会发生变异。如果被引用的对象恰好是烘焙到函数中的对象,它仍然会发生变异。
但你不能改变None
。这是不可改变的。
你可以改变[]
。它是一个列表,列表是可变的。将项目附加到列表会改变列表。
答案 1 :(得分:15)
如果默认值是可变的,None
不是,则该问题才存在。与函数对象一起存储的内容是默认值。调用该函数时,将使用默认值初始化函数的上下文。
a_list = []
只需在当前函数调用的上下文中为名称a_list
指定一个新对象。它不会以任何方式修改None
。
答案 2 :(得分:12)
默认值a_list
(或其他任何默认值)在初始化后存储在函数内部,因此可以以任何方式进行修改:
>>> def f(x=[]): return x
...
>>> f.func_defaults
([],)
>>> f.func_defaults[0] is f()
因此func_defaults
中的值与函数内部的函数相同(并在我的示例中返回以便从外部访问它。
IOW,调用f()
时发生的情况是隐式x = f.func_defaults[0]
。如果随后修改了该对象,您将保留该修改。
相比之下, 赋值函数始终是新的[]
。任何修改都将持续到最后一次引用[]
为止;在下一个函数调用中,创建一个新的[]
。
再说一遍,[]
在每次执行时获取相同的对象并不是真的,但它(在默认参数的情况下)只执行一次然后保存。
答案 3 :(得分:4)
不,good_insert
a_list
中只有一次没有被宣传。
每次调用函数时都不指定a_list
参数,使用默认值并使用并返回list
的新实例,新列表不会替换默认值。
答案 4 :(得分:0)
默认值仅计算一次。
评估的(仅一次)默认值在内部存储(为简单起见,将其命名为x
)。
案例[]
:
当您定义的函数的a_list
默认为[]
时,如果不提供a_list
,则在为函数分配内部变量x
时, 。因此,当您追加到a_list
时,实际上是在追加到x
(因为a_list
和x
现在引用相同的变量)。当您再次调用不带a_list
的函数时,更新后的x
将重新分配给a_list
。
案例None
:
值None
会被评估一次并存储在x
中。如果您没有提供a_list
,则变量x被分配给a_list。但是,您当然不会附加到x
。您将一个空数组重新分配给a_list
。此时x
和a_list
是不同的变量。以同样的方式在不使用a_list
的情况下再次调用该函数时,它首先从None
获取值x
,但随后又将a_list再次分配给空数组。
请注意,对于a_list = []
情况,如果在调用函数时为a_list
提供了显式值,则新参数不会覆盖{{1} },因为它只被评估一次。