为什么defaultdict default_factory默认为None?

wim*_*wim 4 python defaultdict

您不必指定默认工厂(但如果None明确传递则相同)

>>> from collections import defaultdict
>>> defaultdict()
defaultdict(None, {})
>>> defaultdict(None)
defaultdict(None, {})
Run Code Online (Sandbox Code Playgroud)

为什么None呢?然后我们得到这个东西:

>>> dd = defaultdict()
>>> dd[0]
# TypeError: 'NoneType' object is not callable  <-- expected behaviour
# KeyError: 0                                   <-- actual behaviour
Run Code Online (Sandbox Code Playgroud)

它甚至被明确允许,因为如果你尝试从其他对象创建一个默认的dict,defaultdict(0)比如说,有一个失败的检查

TypeError: first argument must be callable or None
Run Code Online (Sandbox Code Playgroud)

我认为类似的东西lambda: None会是一个更好的默认工厂.为什么default_factory可选?我不明白用例.

Ste*_*ski 6

当吉多·范罗苏姆最初提出DefaultDict的是有一个默认值(不像现在defaultdict它使用的函数,而不是一个值)设定在施工期间和只读(也不像defaultdict).

经过一番讨论,Guidio修改了提案.以下是相关要点:

许多人建议使用工厂功能而不是默认值.这确实是一个更好的主意(尽管对于最简单的情况来说稍微麻烦一些).

...

让我们在dict类中添加一个通用的缺失键处理方法,以及一个初始化为的default_factory槽None.

...

[T]他的默认实现是为了我们可以编写而设计的

d = {}
d.default_factory = list
Run Code Online (Sandbox Code Playgroud)

需要注意的重要一点是,新功能不再属于子类.这意味着default_factory在构造函数中设置会破坏现有代码.因此,通过设计设置default_factory必须dict在创建之后发生.它的初始值设置为None,它现在是一个可变属性,因此可以被有意义地覆盖.

经过更多的讨论后,我们决定最好不要将常规dict类型与defaultdict专业化相结合.

然后Steven Bethard 要求澄清构造函数:

default_factory应该是构造函数的参数吗?我看到的三个答案:

  • "没有." 我不是这个答案的忠实粉丝.由于创建defaultdict类型的重点是提供默认值,因此需要两个语句(构造函数调用和default_factory赋值)来初始化这样的字典似乎有点不方便.
  • "是的,它应该跟随所有正常的dict构造函数参数." 这没关系,但有一些错误,比如 defaultdict({1:2})会默默地传递(直到你尝试使用dict,当然).
  • "是的,它应该是唯一的构造函数论点." 这是我最喜欢的,主要是因为我认为这是简单的,我想不出很好的例子,我真正想做的事defaultdict(list, some_dict_or_iterable)defaultdict(list, **some_keyword_args).如果我们需要在以后添加一些dict构造函数args,它也是向前兼容的.

Guido van Rossum 决定:

defaultdict签名采用可选的位置参数,即default_factory,默认为None.剩余的位置和所有关键字参数都传递给dict构造函数.IOW:

d = defaultdict(list, [(1, 2)])
Run Code Online (Sandbox Code Playgroud)

相当于:

d = defaultdict()  
d.default_factory = list  
d.update([(1, 2)])
Run Code Online (Sandbox Code Playgroud)

请注意,当Guido考虑改变dict以提供defaultdict行为时,扩展代码完全反映了它的工作方式.

他还提供了一些上线的理由:

即使将default_factory传递给构造函数,它仍然应该是一个可写属性,因此可以进行内省和修改.在创建后无法更改其默认工厂的defaultdict不太有用.

Bengt Richter 解释了为什么你可能需要一个可变的默认工厂:

我的猜测是,现实中default_factory将用于制作干净的代码以填充字典,然后关闭工厂,如果它要传递到未知的上下文中.然后,这些上下文可以使用旧代码执行上述操作,或者如果值得,可以暂时设置工厂来执行某些工作.紧密耦合的代码我想可以在彼此之间传递工厂启用的dicts.