在實際中使用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