使用defaultdict的注意点

使用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

补充

  1. 官方defaultdict地址:https://docs.python.org/zh-cn/3/library/collections.html#collections.defaultdict

上面说明了: 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

  1. __getitem__() 用于实现self[key] 的求值
  2. 对于key in d,d是一个字典,结果和key in d.keys()是一样的
    Return True if d has a key key, else False.

上一篇
go的依赖管理及编译 go的依赖管理及编译
go的依赖管理及编译依赖管理工具为什么go需要依赖管理工具早期go下载的第三方包都在$GOPATH$/src下,查找时也从$GOPATH$/src/ 中进行查找,如果没有$GOPATH$则会使用$GOROOT$。 而在有多个go项目时,同一
2021-12-03
下一篇
尝试在hexo上进行latex渲染遇到的坑 尝试在hexo上进行latex渲染遇到的坑
尝试在hexo上进行latex渲染遇到的坑自顺利在hexo上安装插件渲染出思维导图后,我有点飘了,正好遇到在写文章时遇到一个地方要写上下标,于是就在想,能不能在hexo上渲染出latex呢? 在查阅了一些文章后,我信心满满的按照教程开始了安
2021-05-30
目录