<p>其他一些人指出,这是Python中“可变默认参数”问题的一个实例。基本原因是默认参数必须存在于函数的“外部”才能传递给它。在</p>
<p>但真正的根源是一个<i>问题</i>与默认参数无关</b>。任何时候,如果修改一个可变的默认值是不好的,你真的需要问问自己:如果一个显式提供的值被修改了,它会是坏的吗?除非有人非常熟悉您的类的内部结构,否则以下行为也会非常令人惊讶(因此会导致bug):</p>
<pre><code>>>> class One(object):
... def __init__(self, my_list=[]):
... self.my_list = my_list
...
>>> alist = ['hello']
>>> one1 = One(alist)
>>> alist.append('world')
>>> one2 = One(alist)
>>>
>>> print(one1.my_list) # Huh? This isn't what I initialised one1 with!
['hello', 'world']
>>> print(one2.my_list) # At least this one's okay...
['hello', 'world']
>>> del alist[0]
>>> print one2.my_list # What the hell? I just modified a local variable and a class instance somewhere else got changed?
['world']
</code></pre>
<p>10次中有9次,如果您发现自己在使用<code>None</code>作为默认值并使用<code>if value is None: value = default</code>的“模式”,那么您不应该这样做。你不应该修改你的论点!参数不应该被视为被调用代码拥有的,除非它被明确地记录为拥有它们。在</p>
<p>在这种情况下(尤其是因为您正在初始化一个类实例,所以可变变量将生存很长时间,并被其他方法和可能从实例中检索它的其他代码使用)我将执行以下操作:</p>
^{pr2}$
<p>现在,您将从作为输入提供的列表初始化类的数据,而不是获取预先存在的列表的所有权。两个独立的实例最终共享同一个列表,也不存在与调用方可能希望继续使用的调用方中的变量共享该列表的危险。它还有一个很好的效果,你的调用者可以提供元组、生成器、字符串、集合、字典、自制的自定义iterable类等,而且你知道你仍然可以依赖它self.my_列表有一个<code>append</code>方法,因为这是你自己做的。在</p>
<p>这里仍然存在一个潜在的问题,如果列表中包含的元素本身是可变的,那么调用者和这个实例仍然会意外地相互干扰。我发现在我的代码中,这并不是一个经常出现的问题(所以我不会自动对所有东西进行深入的复制),但是你必须意识到这一点。在</p>
<p>另一个问题是,如果我的清单可能非常大,副本可能是昂贵的。在那里你必须做出权衡。在这种情况下,也许最好使用传入列表,并使用<code>if my_list is None: my_list = []</code>模式来防止所有默认实例共享同一个列表。但是,如果您这样做,您需要在文档或类的名称中明确指出,调用方正在放弃它们用于初始化实例的列表的所有权。或者,如果您真的只想构建一个列表来封装一个<code>One</code>的实例,也许您应该找出如何将列表的创建封装在<code>One</code>的初始化中,而不是先构造它;毕竟,它实际上是实例的一部分,而不是初始化值。但有时这不够灵活。在</p>
<p>有时候你真的想让别名继续存在,让代码通过改变它们都可以访问的值来进行通信。然而,在我致力于这样的设计之前,我想得很仔细。而且它会让其他人(还有你在X个月后回到代码中时)感到惊讶,所以文档也是你的朋友!在</p>
<p>在我看来,教育新的Python程序员“可变的默认参数”实际上是有害的。我们应该问他们“你为什么要修改你的论点?”(然后指出Python中默认参数的工作方式)。不管这个参数的值是否是一个很好的参数,它通常都不应该被修改。在</p>