我正在学习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
值并且仍然是错误的。
为什么不呢?我错过了什么概念? a_list
每次运行时good_append
如何擦干净?
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]
会发生什么。如果随后修改了该对象,您将保留该修改。
相比之下,函数内部的赋值始终是一个新的[]
。任何修改都将持续到最后一次引用该[]
为止;在下一个函数调用中,创建一个新的[]
。
再说一遍,[]
在每次执行时获取相同的对象并不是真的,但它(在默认参数的情况下)只执行一次然后保留。
看起来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
。这是不可改变的。
你可以改变[]
。它是一个列表,列表是可变的。将项添加到列表会改变列表。
如果默认值是可变的,None
不是,那么问题才存在。与函数对象一起存储的内容是默认值。调用该函数时,将使用默认值初始化函数的上下文。
a_list = []
只是在当前函数调用的上下文中为名称a_list
指定一个新对象。它不会以任何方式修改None
。
不,在qazxsw poi中,qazxsw poi只是初始化一次。
每次调用函数时都不指定good_insert
参数,使用默认值并使用并返回a_list
的新实例,新列表不会替换默认值。