分析python中的collections.defaultdict

函数定义:

def __init__(self, default_factory=None, **kwargs)

使用示例:

from collections import defaultdict

 

场景一:

无参数,默认参数default_factory=None。

d2 = defaultdict() # default_factory为空

print(type(d2)) # <class 'collections.defaultdict'>

d2["a"] = 1

print(d2) # defaultdict(None, {'a': 1})

print(d2["a"]) # 1

print(d2["d"]) # KeyError: 'd'

print(d2.get("d")) # None

 

场景二:

注意:函数定义中的 default_factory 有特殊要求:

first argument must be callable or None

d2 = defaultdict(lambda: "good") # 键值不存在时调用匿名函数返回默认值。

print(type(d2)) # <class 'collections.defaultdict'>

d2["a"] = 1

对比场景一,我们会发现第一个参数为function,因为我们指定了一个callable匿名函数作为参数。

print(d2) # defaultdict(<function <lambda> at 0x000002241AF38048>, {'a': 1})

print(d2["a"]) # 1

# print(d2["d"])

print(d2.get("d")) # None

 

当key不存在时的情况:

我们直接用d2.get("d")去获取一个不存在的键值,需要注意的是此时并未调用defaultdict的__getitem__()方法(d2["d"]才会调用,此时我们故意先注释掉上一行的代码,就是为了突出这个现象),返回值为None。这是为什么呢,难道我们设置的方法没起作用吗?

真实情况是我们传入的方法还未被调用,结果还未返回,相当于字典中还没有添加默认值"good".

下面我们去掉注释:

 

d2 = defaultdict(lambda: "good")

print(type(d2)) # <class 'collections.defaultdict'>

d2["a"] = 1

print(d2) # defaultdict(<function <lambda> at 0x000002241AF38048>, {'a': 1})

print(d2["a"]) # 1

print(d2.get("d")) # None

print(d2["d"]) # "good"

print(d2.get("d")) # "good"

在print(d2["d"])语句后的d2.get("d")就有了我们设定的默认值,因为我们传入的方法被调用了。

 

那么我们传入的方法是何时被调用的呢?

当我们调用collections.defaultdict()方法时,查看源码我们会发现,当d2["d"]调用了__getitem__()方法,并且传入的时一个不存在的key时,我们传入的无参工厂方法会被调用,用来生产一个新的值,并返回,这个新的值就是"good"。

 

 

分析python中的collections.defaultdict

 

__getitem__()

分析python中的collections.defaultdict

仔细查看源码中的方法,会发现在调用__getitem__()方法,传入一个不存在的key时,会调用另一个方法。

 

分析python中的collections.defaultdict

 

所以说真正处理键值不存在的方法时在这个__missing__(self, key)方法里,self[key] = self.default_factory()才是真正调用我们传入的方法,为这个不存在的键值赋予默认值并添加到字典中。

至此,我们终于弄清了defaultdict的原理。而传入的工厂方法又是什么呢,下一篇文章再来分享。