分析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"。
__getitem__()
仔细查看源码中的方法,会发现在调用__getitem__()方法,传入一个不存在的key时,会调用另一个方法。
所以说真正处理键值不存在的方法时在这个__missing__(self, key)方法里,self[key] = self.default_factory()才是真正调用我们传入的方法,为这个不存在的键值赋予默认值并添加到字典中。
至此,我们终于弄清了defaultdict的原理。而传入的工厂方法又是什么呢,下一篇文章再来分享。