Bootstrap

Python拓展dict类

问题描述

在使用python字典的时候经常会遇到一个问题,就是多层字典需要逐层创建。比如:

a = {'a': 'a'}
a['b']['c'] = 1  # 这里会报错,dict不会自动创建多层结构

所以这里就给出一个dict类的拓展方法

解决方案

先给出最终方案,方便日后直接抄作业,后面会给出详解。

class compositedict(dict):
    def __init__(self, seq=None, **kwargs):
        super(compositedict, self).__init__()
        if seq is None:
            pass
        elif isinstance(seq, dict):
            for k, v in seq.items():
                self[k] = compositedict(v) if isinstance(v, dict) else v
        else:
            for k, v in seq:
                self[k] = compositedict(v) if isinstance(v, dict) else v
        for k, v in kwargs.items():
            self[k] = compositedict(v) if isinstance(v, dict) else v

    def __getitem__(self, item):
        if not self.__contains__(item):
            self[item] = compositedict()
        return super(compositedict, self).__getitem__(item)

自动逐层创建字典

[]获取键值的时候判断是否存在,不存在则创建新compositedict,保证能递归到更深层

class compositedict(dict):
	def __getitem__(self, item):
		if not self.__contains__(item):
			self[item] = compositedict()
		return super(compositedict, self).__getitem__(item)

验证:

a = compositedict()
a['a']['b']['c'] = 'd'
print(a)
print(type(a['b']))

输出:
在这里插入图片描述

实例创建及类型转换

上面只是实现了在深层索引的时候自动创建字典,但是初始化创建及类型转换的时候仍然会存在问题,比如:

a = {'xxx': {'yyy': {'zzz': 'a'}}}
b = compositedict(a)
b['xxx']['vvv']['www'] = 'b'

上面的代码会报错KeyError,因为b['xxx']类型仍然是dict,因此我们要做的是将变量每一层的dict都替换为compositedict
要解决问题,我们先看一下dict的__init__方法:

    def __init__(self, seq=None, **kwargs): # known special case of dict.__init__
        """
        dict() -> new empty dictionary
        dict(mapping) -> new dictionary initialized from a mapping object's
            (key, value) pairs
        dict(iterable) -> new dictionary initialized as if via:
            d = {}
            for k, v in iterable:
                d[k] = v
        dict(**kwargs) -> new dictionary initialized with the name=value pairs
            in the keyword argument list.  For example:  dict(one=1, two=2)
        # (copied from class doc)
        """
        pass

总结来看,层级结构中出现dict会有以下几种情况:

  • dict({'a': {'b': 'c'}})
  • dict([('a', {'b': 'b'}), ('c', 'd')])
  • dict(a={'b': 'b'})

针对以上情况,基于已有的compositedict重载__init__方法:

class compositedict(dict):
    def __init__(self, seq=None, **kwargs):
        super(compositedict, self).__init__()
        if seq is None:  # 需要特殊处理None情况
            pass
        elif isinstance(seq, dict):  # 处理seq为dict的情况
            for k, v in seq.items():
                self[k] = compositedict(v) if isinstance(v, dict) else v
        else:
            for k, v in seq:  # 处理seq为列表结构的情况
                self[k] = compositedict(v) if isinstance(v, dict) else v
        for k, v in kwargs.items():  # 对kwargs中的键值对同样递归处理
            self[k] = compositedict(v) if isinstance(v, dict) else v

    def __getitem__(self, item):
        if not self.__contains__(item):
            self[item] = compositedict()
        return super(compositedict, self).__getitem__(item)

验证:

a = compositedict({'a': {'b': {'c': 'c'}}})
print(type(a['a']))
print(type(a['a']['b']))
print(type(a['a']['d']))
print(a)
b = compositedict([('a', {'b': 'b'})])
print(type(b['a']))
c = compositedict(a={'a': 'b'})
print(type(c['a']))

输出:
在这里插入图片描述

潜在问题

这个解决方案有个潜在问题:可能会创建非期望的空字典
举个例子:

a = compositedict()
b = 0 if a['a'].get('b') else 1
print(a)

输出:{'a': {}}
由此我们可以看到,a在判断语句中值被改变了,在某些情况下这是我们不期望的结果,所以使用的时候需注意。

;