使用defaultdict的注意点
总结
前提: 下面提到的dict都是在要为value里添加数据时才会添加key,也就是说不会存在添加了key但value为空的情况
对于value是list的dict,通常对value(list)使用索引时,会使用key in items
来判断在items中是否存在此key,在存在的情况下再对items[key]使用索引,以免使用索引时越界。
但是,对于默认值是list的defaultdict,使用items[key]会为此key初始化为默认值,如果在不当心使用items[key]后再进行key in items
就会返回True,但此时items[key]=[],使用索引会越界。
原因是对于 defaultdict,使用 items[key] 会调用 __getitem__()
,继而调用__missing__()
,从而为不存在的key初始化默认值。而dict不会为不存在的key进行初始化,因此存在的key的value始终不为[],不会发生越界。
踩坑场景
在多叉树的前序遍历中,使用defaultdict来保存某个父节点的子节点,并且需要多次调用此函数来返回前序遍历的结果,同时还需要判断当前父节点的子节点列表中的最后一个值是否为None。
self.children_list = collections.defaultdict(list)
def getInheritanceOrder(self) -> List[str]:
def if_death(x):
# 预想中如果x不存在,就会直接返回False,不会执行and后面的语句
if x in self.children_list and self.children_list[x][-1] == None:
return True
else:
return False
def Successor(x):
if not if_death(x):
ans.append(x)
# 对父节点的子节点进行dfs遍历,如果为[]就不会进行
for children in self.children_list[x]:
# 跳过标记位子
if children == None:
break
Successor(children)
ans = []
Successor(self.kingName)
return ans
在同一个程序中第二次调用getInheritanceOrder这个函数时,在if_death的判断语句中抛出了越界异常,经排查,发现在对叶节点进行self.children_list[key]遍历时,虽然通过默认值self.children_list[x]为[]不会进入for,但是defaultdict还是为把该key进行初始化放入字典。
也就是说自己既想利用defaultdict的默认值特性免去在子节点遍历前对key是否存在进行判断,又想在事后让该字典对不存在的key返回False,可谓渣男行为了。。
改进后的代码
def if_death(x):
# 使用len进行判断
if len(self.children_list[x])>0 and self.children_list[x][-1]==None:
return True
else:
return False
补充
上面说明了:
defaultdict
是内置dict
类的子类。它重载了一个方法并添加了一个可写的实例变量。其余的功能与dict
类相同。本对象包含一个名为
default_factory
的属性,构造时,第一个参数用于为该属性提供初始值,默认为None
。所有其他参数(包括关键字参数)都相当于传递给dict
的构造函数。
defaultdict
对象除了支持标准dict
的操作,还支持以下方法作为扩展:
__missing__
(key)如果
default_factory
属性为None
,则调用本方法会抛出KeyError
异常,附带参数 key。如果default_factory
不为None
,则它会被(不带参数地)调用来为 key 提供一个默认值,这个值和 key 作为一对键值对被插入到字典中,并作为本方法的返回值返回。如果调用default_factory
时抛出了异常,这个异常会原封不动地向外层传递。在无法找到所需键值时,本方法会被dict
中的__getitem__()
方法调用。无论本方法返回了值还是抛出了异常,都会被__getitem__()
传递。注意,__missing__()
不会 被__getitem__()
以外的其他方法调用。意味着get()
会像正常的 dict 那样返回None
,而不是使用default_factory
。
__getitem__()
用于实现self[key]
的求值- 对于
key in d
,d是一个字典,结果和key in d.keys()
是一样的
ReturnTrue
if d has a key key, elseFalse
.