如何优雅的生成及遍历python嵌套字典

嵌套字典生成

方法一:定义类


class Vividict(dict):

def __missing__(self, key):

value = self[key] = type(self)()

return value

解释:

  • 第一行:class后面紧接着是类名,即Vividict,类名通常是大写开头的单词,紧接着是(dict),表示该类是dict类继承下来的。

我们可以使用dir(dict)查看dict的方法

In[22]: print(dir(dict))

['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

同理,可以查看Vividict的方法

In[23]: print(dir(Vividict))

['__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__missing__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

比较两者可以发现,Vividict的方法比dict的方法多了一个missing方法,也就是我们添加的方法。所以这就是继承,继承最大的好处是子类获得了父类的全部功能,而不必重新造*。

  • 第二行:python魔法方法中的自定义序列,类似于定义一个函数。missing在字典的子类中使用,它定义了当试图访问一个字典中不存在的键时的行为(目前为止是指字典的实例,例如我有一个字典 d , “george” 不是字典中的一个键,当试图访问 d[‘george’] 时就会调用 d.missing(“george”),结果为{} )。

  • 第三行,第四行:访问字典中不存在的键(key)时,返回空字典作为其返回值(value)

例如:

In[17]: a = dict()

In[18]: type(a)()

Out[18]:

{}

注意:

  • 特殊方法“missing”前后有两个下划线!!!
  • 和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

使用:


# coding=utf-8

#导入模块

import os, openpyxl

import pprint

from pandas import DataFrame

#pprint模块可以输出漂亮的字典结构,但是不利于后期利用R作图

#DataFrame可以将字典结构转为数据框输出,方便后期利用R作图

#切换工作路径

os.chdir(r'F:\pycharm_project\mutation_count')

#读取excel表格

wb = openpyxl.load_workbook('东方肝胆数据综合.xlsx')

sheet = wb.active

#定义类

class Vividict(dict):

def __missing__(self, key):

value = self[key] = type(self)()

return value

#实例化

d = Vividict()

#字典初始化,赋初值0

for i in range(2,sheet.max_row+1):

d[sheet.cell(row=i, column=1).value][sheet.cell(row=i, column=15).value] = 0

#累加统计各个样本各种突变类型的数目

for i in range(2,sheet.max_row+1):

d[sheet.cell(row=i, column=1).value][sheet.cell(row=i, column=15).value] +=1

pprint.pprint(d)

#输出字典结构

pprint.pprint(d)

{'PDC1279A_vs_PDC1279': {'UTR3': 9,

'UTR5': 4,

'downstream': 5,

'exonic': 149,

'intergenic': 170,

'intronic': 163,

'ncRNA_exonic': 17,

'ncRNA_intronic': 23,

'splicing': 2,

'upstream;downstream': 2},

'PDC1279C_vs_PDC1279': {'UTR3': 11,

'UTR5': 13,

'downstream': 1,

'exonic': 174,

'intergenic': 189,

'intronic': 172,

'ncRNA_exonic': 24,

'ncRNA_intronic': 36,

'splicing': 4,

'upstream': 2,

'upstream;downstream': 2}}

#输出数据框结构,缺损的元素用 NaN补齐

frame = DataFrame(d)

print(frame)

PDC1279A_vs_PDC1279 PDC1279C_vs_PDC1279 \

UTR3 9.0 11.0

UTR5 4.0 13.0

downstream 5.0 1.0

exonic 149.0 174.0

exonic;splicing NaN NaN

intergenic 170.0 189.0

intronic 163.0 172.0

ncRNA_exonic 17.0 24.0

ncRNA_intronic 23.0 36.0

ncRNA_splicing NaN NaN

splicing 2.0 4.0

upstream NaN 2.0

upstream;downstream 2.0 2.0

方法二:使用defaultdict()

两个维度字典:


from collections import defaultdict

d = defaultdict(defaultdict)

d[1][2] = 3

等价于:


from collections import defaultdict

def nested_dict_factory():

return defaultdict(int)

def nested_dict_factory2():

return defaultdict(nested_dict_factory)

db = defaultdict(nested_dict_factory2)

当然,第一种方法简洁的多!

要获得更多维度,你可以(三维):


from collections import defaultdict

d = defaultdict(lambda :defaultdict(defaultdict))

d[1][2][3] = 4

使用defaultdict任何未定义的key都会默认返回一个根据method_factory参数不同的默认值, 而相同情况下dict()会返回KeyError.

python中lambda存在意义就是对简单函数的简洁表示

实际上 defaultdict也是通过missing方法实现的。defaultdict在dict的基础上添加了一个missing(key)方法, 在调用一个不存的key的时候, defaultdict会调用missing, 返回一个根据default_factory参数的默认值, 所以不会返回Keyerror.


In[35]: print(dir(defaultdict))

['__class__', '__contains__', '__copy__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__missing__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'default_factory', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

嵌套字典的遍历

方法一:一层一层的嵌套迭代,从而实现遍历


for key,value in d.items():

for key2, val2 in value.items():

print (key2, val2)

在类中定义walk方法实现嵌套字典的遍历


class Vividict(dict):

def __missing__(self, key):

value = self[key] = type(self)()

return value

def walk(self):

for key, value in self.items():

if isinstance(value, Vividict):

for tup in value.walk():

yield (key,) + tup

else:

yield key, value

解释:
第1-4行:上面已经解释过了
第5-11行:定义一个walk函数,并对字典items对象的key和value进行遍历,isinstance用于判断对象类型,如果value是一个字典,那么对value调用walk()方法继续进行遍历,一层一层将key,value存储在元祖中()。当最里面一层,即else情况,输出key,value。整个过程即将字典数据结构扁平化为元祖

此时,我们可以这样来遍历字典(输出元祖)

#打印整个元祖

for tup in d.walk():

print(tup)

('PDC1279_vs_PDC1279C6', 'downstream', 3)

('PDC1279_vs_PDC1279C6', 'UTR3', 11)

('PDC1279_vs_PDC1279C6', 'intronic', 164)

('PDC1279_vs_PDC1279C6', 'splicing', 4)

**这就是扁平化的字典**

#打印元祖的第3列

for tup in d.walk():

print(tup[2])

参考

(1)https://ask.helplib.com/229754

(2)Python魔法方法指南(推荐阅读)

http://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html

上一篇:Python,双重自动枚举defaultdict


下一篇:python defaultdict模块