为什么在Python中,空字典作为默认值是一个危险的选择?

243

我在Python函数的可选参数中将字典作为默认值,使用Sublime包中的pylint提示这是危险的。有人能解释一下为什么吗?还有,使用None作为替代方案是否更好?

我将一个字典作为Python函数的可选参数的默认值,但是pylint(使用Sublime package)告诉我这样做是危险的。有人可以解释一下原因吗?另外,使用None是否是更好的选择?

4
将空列表作为默认参数传递的问题在于它将在函数的所有调用之间共享 -- 请参见 https://docs.python.org/3/tutorial/controlflow.html#default-argument-values 中的“重要警告”。 - hamed
2个回答

304

让我们来看一个例子:

def f(value, key, hash={}):
    hash[value] = key
    return hash

print(f('a', 1))
print(f('b', 2))

你可能期望输出的结果是:

{'a': 1}
{'b': 2}

但实际输出:

{'a': 1}
{'a': 1, 'b': 2}

28
我们如何才能实现一个默认值与后续代码相匹配,而不是说“如果h是None:h = {}”,然后继续执行代码? - Ricky Levi
15
我有点晚了,但我想感谢你的例子。在Python中,我不知道空列表/字典为什么被认为是“危险”的,这让我很困惑。这篇文章对我很有帮助。 - Mandemon
4
@alper 这不是全局变量,你只能在函数的作用域内访问它,但如果没有提供默认值,则引用你定义为字面值的字典。避免这种情况的一种方法是:def func(hash=None): if hash == None: hash = {} ... - marxus
25
我只想说这是一个真正的 Python WTF。谁会认为这可能是理智的行为呢? - Timmmm
10
请注意,hash=dict()hash={}具有相同的行为。 - aydow
显示剩余6条评论

276

只有在您的函数会修改参数时才会出现危险。如果您修改了默认参数,则该参数会一直存在于下一次调用之前,因此您的“空”字典将在除第一次调用之外的其他调用中开始包含值。

是的,在这种情况下使用 None 既安全又常规。


2
如果函数不修改参数,我们是否仍应该在最佳实践的名称中使用None作为默认值? - NightFurry
3
@NightFurry:我在这里犹豫不决,None通常是最好的选择,但也许并非100%的情况。请根据您的判断力做出决定,但如果您无法决定,None是一个安全的选择。 - John Zwinck
8
如果其他人偶然看到这个问题,请查看此答案,了解有关函数参数中可选字典的更多信息:链接。我认为这是一个更好的讨论,可以解决如何处理此问题。基本上,现在应该对一个可选的{key: value}映射进行类型提示,例如arg: Optional[Mapping[str, str]] = None - Inarus Lynx
3
如果函数不修改参数但泄漏了对它的引用(通过返回它、产生它、将其作为参数调用其他函数等),那么同样的问题可能会发生。我建议始终使用None,即使函数被仔细编写以避免此类错误,纯粹是为了使对函数的看似无害的更改不能破坏它。 - kaya3
3
这听起来像是 Python 的一个 bug,据我所知其他动态编程语言不会表现出这种行为。 - 27px
1
对于我们的情况,使用空元组作为默认参数是一举两得的解决方案。我们通过将无操作的情况委托给迭代参数的内容,来跳过None分支。元组是不可变的,因此我们可以防止上述类别的错误。另外还有一个额外的好处:简化的类型提示。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接