Python中defaultdict的使用
在实际中使用defaultdict
会十分效率地为我们构建不同的数据格式,通常需要好几层循环构建的数据如果巧用默认字典的话使用一层循环便可实现,这一点笔者深有体会!本文就为大家总结一下使用defaultdict
构建数据的一些实例
整合相同类型的数据
现有如下的数据:
res = [
{"ID":111,"HOUSE":1},
{"ID":222,"HOUSE":1},
{"ID":333,"HOUSE":1},
{"ID":444,"HOUSE":2},
{"ID":555,"HOUSE":2},
]
我们需要将相同HOUSE
的ID
整合到一起,然后以每个ID
为key,找到每个ID对应的详情,想要的结果如下(每个ID对应的detail信息是从其他接口获取的)
:
{
1: {
111: {111:"detail111"},
222: {222:"detail222"},
333: {333:"detail333"}
},
2: {
444: {444:"detail444"},
555: {555:"detail555"}
}
}
这里就不介绍其他的方法了,有兴趣的同学可以自己试试,这里直接给出defaultdict的方法:
from collections import defaultdict
# 这里defaultdict默认的元素是dict
ret = defaultdict(dict)
res = [
{"ID":111,"HOUSE":1},
{"ID":222,"HOUSE":1},
{"ID":333,"HOUSE":1},
{"ID":444,"HOUSE":2},
{"ID":555,"HOUSE":2},
]
for i in res:
# 可以从其他接口中获取ID对应的detail,这里就省略了,直接用固定的字符串代替
detail = f"detail{i['ID']}"
ret[i["HOUSE"]].update({i["ID"]:{}})
ret[i["HOUSE"]][i["ID"]].update({i["ID"]:detail})
print(ret)
# defaultdict(<class 'dict'>, {1: {111: {111: 'detail111'}, 222: {222: 'detail222'}, 333: {333: 'detail333'}}, 2: {444: {444: 'detail444'}, 555: {555: 'detail555'}}})
可以看到最终得到一个defaultdict对象
,是一个自定义的字典
,其实它可以被强转为一个dict对象,但是强转没有意义,将得到的结果可以直接当做是一个字典对象就可以。
我们看一下默认字典的声明:
from collections import defaultdict
ret = defaultdict(dict)
这样的声明之后,我们可以将ret看成一个字典对象,这个字典对象中默认的元素
是dict
(就是上面defaultdict中的参数),声明之后我们可以将ret当做是一个字典来进行操作,对ret中的元素可以进行字典的操作(defaultdict中用什么元素就可以使用对应的方法,比如上面例子用到了dict的update方法)。
其他的例子
如果上面的说明你还没有看明白的话,笔者这里列举一些常见的例子帮助大家理解。
合并字符串
下面这个例子其实是笔者之前遇到的一个面试题,在此分享一下:
有如下两个列表:
a = ['a,1', 'b,3,22', 'c,3,4', 'f,5', ]
b = ['a,2', 'b,4', 'w,12', 'd,2','c,123']
需要将两个列表中有相同字母的字符串合并,合并后的结果如下:
c = ['a,1,2', 'b,3,22,4', 'c,3,4,123', 'f,5', 'w,12', 'd,2']
当初年轻的我是这么做的:
# -*- coding:utf-8 -*-
a = ['a,1', 'b,3,22', 'c,3,4', 'f,5', ]
b = ['a,2', 'b,4', 'w,12', 'd,2','c,123']
dic = {}
for i in a:
dic[i[0]] = i
print(dic)
for i in b:
if i[0] in dic:
dic[i[0]] += i[1:]
else:
dic[i[0]] = i
print(dic)
print(list(dic.values()))
其实使用defaultdict的话会很方便:
# -*- coding:utf-8 -*-
from collections import defaultdict
a = ['a,1', 'b,3,22', 'c,3,4', 'f,5', ]
b = ['a,2', 'b,4', 'w,12', 'd,2','c,123']
dic = defaultdict(str)
for i in a:
dic[i[0]] = i
dic = dict(dic)
print(dic)
for i in b:
if i[0] in dic:
dic[i[0]] += i[1:]
else:
dic[i[0]] = i
print(dic)
print(list(dic.values()))
这里可以看到:defaultdict中的元素是str,因此我们在构建数据的时候可以使用字符串拼接的方式。
统计颜色数量
现有如下的数据:
lis = [('红色',1),('白色',2),('绿色',3),('紫色',1),('红色',1),('白色',1),('红色',1),('粉色',1),]
统计一下每个颜色的数量,期望的结果如下:
dic = {'红色': 3, '白色': 3, '绿色': 3, '紫色': 1, '粉色': 1}
默认元素选择list,实现方法如下:
# -*- coding:utf-8 -*-
from collections import defaultdict
### 将每种球与其总数量统计出来
### {'红色':3,'白色':3,...}
lis = [('红色',1),('白色',2),('绿色',3),('紫色',1),('红色',1),('白色',1),('红色',1),('粉色',1),]
dic = defaultdict(list)
for i in lis:
dic[i[0]].append(i[1])
dic = dict(dic)
print(dic)
for i in dic:
dic[i] = sum(dic[i])
print(dic)
默认元素为int,三次登陆失败锁定的简单例子
userinfo
文件中的内容如下,作为登陆的用户名密码测试文件:
whw|123
wanghw|123
www|123
locked
文件中存放的是被锁定的用户名。
实现一个简单的三次登陆失败锁定的例子:
# -*- coding:utf-8 -*-
from collections import defaultdict
# 默认value设置成0
dic = defaultdict(int)
# 这里设置一个flag,while循环的额条件是flag;
# 如果循环内还有一层循环,跳出内层循环若需要跳出整个循环可以将flag设置成False!
flag = True
while flag:
username = input('用户名:').strip()
password = input('密码:').strip()
# 先判断账号有没有被锁定
with open('locked','r',encoding='utf-8')as f:
for line in f:
if username == line.strip():
print('该账号已被锁定!')
flag = False
break
# 默认字典 —— 确保 “每个用户都有三次机会”
if dic[username] < 2:
with open('userinfo','r',encoding='utf-8')as f:
for line in f:
usr,pwd = line.strip().split('|')
if usr == username and pwd == password:
print('登陆成功!')
# 如果想登陆成功后退出while大循环,可以将flag设置为False
flag = False
# 这个break只是退出for循环
break
else:
dic[username] += 1
print('登录失败!用户名或密码错误!')
else:
with open('locked','a',encoding='utf-8')as f:
f.write(username+'\n')
print('账号被锁定!')
break