问题描述
在使用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在判断语句中值被改变了,在某些情况下这是我们不期望的结果,所以使用的时候需注意。