为什么使用`arg = None`修复Python的可变默认参数问题?

问题描述 投票:22回答:4

我正在学习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如何擦干净?

python function arguments mutable
4个回答
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]会发生什么。如果随后修改了该对象,您将保留该修改。

相比之下,函数内部的赋值始终是一个新的[]。任何修改都将持续到最后一次引用该[]为止;在下一个函数调用中,创建一个新的[]

再说一遍,[]在每次执行时获取相同的对象并不是真的,但它(在默认参数的情况下)只执行一次然后保留。


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。这是不可改变的。

你可以改变[]。它是一个列表,列表是可变的。将项添加到列表会改变列表。


15
投票

如果默认值是可变的,None不是,那么问题才存在。与函数对象一起存储的内容是默认值。调用该函数时,将使用默认值初始化函数的上下文。

a_list = []

只是在当前函数调用的上下文中为名称a_list指定一个新对象。它不会以任何方式修改None


4
投票

不,在qazxsw poi中,qazxsw poi只是初始化一次。

每次调用函数时都不指定good_insert参数,使用默认值并使用并返回a_list的新实例,新列表不会替换默认值。

© www.soinside.com 2019 - 2024. All rights reserved.