data = { 'a_b_h':1, 'a_b_i':2, 'a_c_j':3, 'a_d':4, 'a_c_k':5, 'a_e':6 }
new_data = {}
for key, value in data.items(): keys = key.split('_') tmp = new_data last = len(keys) - 1# 最后一个 key 的索引值 for i, k inenumerate(keys): if i == last: tmp[k] = value continue if k notin tmp: sub_tmp = {} tmp[k] = sub_tmp tmp = sub_tmp else: tmp = tmp[k]
这也是群友给出的第一版答案,这样写并没有多大问题,但是代码比较繁琐,肯定还有优化空间。
我们可以只使用一个中间变量即可,进一步优化:
1 2 3 4 5 6 7 8
for field, value in data.items(): keys = field.split('_') tmp = new_data last = len(keys) - 1 for i, k inenumerate(keys): if k notin tmp: tmp[k] = {} if i < last else value tmp = tmp[k] # 将内层 dict 传给 tmp
上面这个代码看似很简洁了,但是仍然还有两个 if 判断,如果不是使用了三元表达式的话,还会更多行。
所以可以进一步优化:
1 2 3 4 5 6
for field, value in data.items(): keys = field.split('_') tmp = new_data for k in keys[:-1]: tmp = tmp.setdefault(k, {}) tmp[keys[-1]] = value
我们省略掉了 last 来判断最后一个字符的索引,直接通过 keys[:-1] 避开最后一个字符,末尾再单独生成数字键值对。
这里还使用字典的一个内置方法 —— setdefault。
dict.setdefault(key, default=None) 方法和 get 方法类似,只是如果键不存在于字典中,不仅会返回 default 参数的值,还同时会用该值自动生成一个键值对。