詳解Python中dbm模塊和shelve模塊的使用
dbm
DBM(DataBase Manager)是一種文件系統(tǒng),專(zhuān)門(mén)用于鍵值對(duì)的存儲(chǔ),最初是在 Unix 平臺(tái)實(shí)現(xiàn),現(xiàn)在其它平臺(tái)也可以用。對(duì)于 KV 模型,DBM 提供了一個(gè)輕量級(jí)、高效的存儲(chǔ)解決方案。
總的來(lái)說(shuō),DBM 具有如下特點(diǎn):
- 簡(jiǎn)單快速:非常簡(jiǎn)單易用,讀取和寫(xiě)入操作都很快,適合存儲(chǔ)少量數(shù)據(jù)。
- 鍵值對(duì)存儲(chǔ):數(shù)據(jù)是以鍵值對(duì)形式存儲(chǔ)的,你可以像操作 Python 字典一樣。
- 文件存儲(chǔ):數(shù)據(jù)存在具體的文件中,可以輕松地備份和轉(zhuǎn)移。
- 不支持復(fù)雜查詢:如果需要執(zhí)行復(fù)雜查詢或需要關(guān)系型數(shù)據(jù)庫(kù)的功能,DBM 可能不是一個(gè)好選擇。
而 Python 標(biāo)準(zhǔn)庫(kù)提供了一個(gè) dbm 模塊,它實(shí)現(xiàn)了 DBM 文件系統(tǒng)的功能,來(lái)看一下它的用法。
import dbm # 第一個(gè)參數(shù)是文件名 # 第二個(gè)參數(shù)是模式,有以下幾種 # r:只讀,要求文件必須存在,默認(rèn)就是這個(gè)模式 # w:可讀可寫(xiě),要求文件必須存在 # c:可讀可寫(xiě),文件不存在會(huì)創(chuàng)建,存在則追加 # n:可讀可寫(xiě),文件不存在會(huì)創(chuàng)建,存在則清空 # 第三個(gè)參數(shù)是權(quán)限,用八進(jìn)制數(shù)字表示,默認(rèn) 0o666,即可讀可寫(xiě)不可執(zhí)行 db = dbm.open("store", "c") # 打開(kāi)文件就可以存儲(chǔ)值了,key 和 value 必須是字符串或 bytes 對(duì)象 db["name"] = "S せんせい" db["age"] = "18" db[b"corporation"] = "小摩".encode("utf-8") # 關(guān)閉文件,將內(nèi)容寫(xiě)到磁盤(pán)上 db.close()
非常簡(jiǎn)單,就像操作字典一樣,并且 key 是唯一的,如果存在則替換。執(zhí)行完后,當(dāng)前目錄會(huì)多出一個(gè) store.db 文件。
我們打開(kāi)它,然后讀取剛才寫(xiě)入的鍵值對(duì)。
import dbm db = dbm.open("store", "c") # 獲取所有的 key,直接返回一個(gè)列表 print(db.keys()) """ [b'corporation', b'name', b'age'] """ # 判斷一個(gè) key 是否存在,key 可以是字符串或 bytes 對(duì)象 print("name" in db, "NAME" in db) """ True False """ # 獲取一個(gè) key 對(duì)應(yīng)的 value,得到的是 bytes 對(duì)象 print(db["name"].decode("utf-8")) print(db[b"corporation"].decode("utf-8")) """ S せんせい 小摩 """ # key 如果不存在,會(huì)拋出 KeyError,我們可以使用 get 方法 print(db.get("NAME", b"unknown")) """ b'unknown' """ # 當(dāng)然也可以使用 setdefault 方法,key 不存在時(shí),自動(dòng)寫(xiě)進(jìn)去 print(db.setdefault("gender", b"female")) """ b'female' """ print(db["gender"]) """ b'female' """
非常簡(jiǎn)單,當(dāng)你需要存儲(chǔ)的數(shù)據(jù)量不適合放在內(nèi)存中,但又沒(méi)必要引入數(shù)據(jù)庫(kù),那么不妨試試使用 dbm 模塊吧。
當(dāng)然啦,dbm 雖然很方便,但它只能持久化 bytes 對(duì)象,字符串也是轉(zhuǎn)成 bytes 對(duì)象之后再存儲(chǔ)的。所以除了 dbm 之外,還有一個(gè)標(biāo)準(zhǔn)庫(kù)模塊 shelve,它可以持久化任意對(duì)象。
shelve
shelve 的使用方式和 dbm 幾乎是一致的,區(qū)別就是 shelve 的序列化能力要更強(qiáng),當(dāng)然速度自然也就慢一些。
import shelve # 第二個(gè)參數(shù)表示模式,默認(rèn)是 c # 因此文件不存在會(huì)創(chuàng)建,存在則追加 sh = shelve.open("shelve") sh["name"] = ["S 老師", "高老師", "電烤??架"] sh["age"] = {18} sh["job"] = {"tutu": "大學(xué)生", "xueer": "醫(yī)生"} # 關(guān)閉文件,刷到磁盤(pán)中 sh.close()
執(zhí)行完之后,本地會(huì)多出一個(gè) shelve.db 文件,下面來(lái)讀取它。
import shelve sh = shelve.open("shelve") print(sh["name"]) print(sh["name"][2] == "電烤??架") """ ['S 老師', '高老師', '電烤??架'] True """ print(sh["age"]) """ {18} """ print(sh["job"]) """ {'tutu': '大學(xué)生', 'xueer': '醫(yī)生'} """ sh.close()
讀取出來(lái)的就是原始的對(duì)象,我們可以直接操作它。
然后自定義類(lèi)的實(shí)例對(duì)象也是可以的。
import shelve class People: def __init__(self, name, age): self.name = name self.age = age @property def print_info(self): return f"name is {self.name}, age is {self.age}" sh = shelve.open("shelve") p = People("群主", 58) # 將類(lèi)、和該類(lèi)的實(shí)例對(duì)象存儲(chǔ)進(jìn)去 sh["People"] = People sh["p"] = p sh.close()
執(zhí)行完之后,我們打開(kāi)它。
import shelve sh = shelve.open("shelve") # 需要注意的是,People 是我們自己定義的類(lèi) # 如果你想要將其還原出來(lái),那么該類(lèi)必須要出現(xiàn)在當(dāng)前的命名空間中 try: sh["People"] except AttributeError as e: print(e) """ Can't get attribute 'People' on <module ...> """ class People: def __init__(self, name, age): self.name = name self.age = age @property def print_info(self): return f"name is {self.name}, age is {self.age}" print(sh["People"] is People) """ True """ print(sh["p"].print_info) """ name is 群主, age is 58 """ print(sh["People"]("群主", 38).print_info) """ name is 群主, age is 38 """
這就是 shelve 模塊,非常強(qiáng)大,當(dāng)然它底層也是基于 pickle 實(shí)現(xiàn)的。如果你不需要存儲(chǔ)復(fù)雜的 Python 對(duì)象,只需要存儲(chǔ)字符串的話,那么還是推薦 dbm。
然后在使用 shelve 的時(shí)候,需要注意里面的一個(gè)坑。
import shelve # 打開(kāi)文件,設(shè)置鍵值對(duì) sh = shelve.open("shelve") sh["name"] = "古明地覺(jué)" sh["score"] = [80, 80, 80] sh.close() # 重新打開(kāi)文件,修改鍵值對(duì) sh = shelve.open("shelve") sh["name"] = "芙蘭朵露" sh["score"].append(90) sh.close() # 再次重新打開(kāi)文件,查看鍵值對(duì) sh = shelve.open("shelve") print(sh["name"]) print(sh["score"]) """ 芙蘭朵露 [80, 80, 80] """ sh.close()
第一次打開(kāi)文件創(chuàng)建兩個(gè)鍵值對(duì),第二次打開(kāi)文件將鍵值對(duì)修改,第三次打開(kāi)文件查看鍵值對(duì)。但是我們發(fā)現(xiàn) sh["name"] 變了,而 sh["score"] 卻沒(méi)變,這是什么原因?
當(dāng)我們修改 name 時(shí),采用的是直接賦值的方式,會(huì)將原本內(nèi)存里的值給替換掉。而修改 score 時(shí),是在原有值的基礎(chǔ)上做 append 操作,它的內(nèi)存地址并沒(méi)有變。
所以可變對(duì)象在本地進(jìn)行修改,shelve 默認(rèn)是不會(huì)記錄的,除非創(chuàng)建新的對(duì)象,并把原有的對(duì)象給替換掉。所以 sh["score"].append(90) 之后,sh["score"] 仍是 [80, 80, 80],而不是 [80, 80, 80, 90]。
因?yàn)?shelve 沒(méi)有記錄對(duì)象自身的修改,如果想得到期望的結(jié)果,一種方法是把對(duì)象整體換掉。也就是讓 sh["score"] = [80, 80, 80, 90],這樣等于是創(chuàng)建了一個(gè)新的對(duì)象并重新賦值,是可行的。
或者你在打開(kāi)文件的時(shí)候,多指定一個(gè)參數(shù) writeback。
import shelve # 打開(kāi)文件,設(shè)置鍵值對(duì) sh = shelve.open("shelve") sh["name"] = "古明地覺(jué)" sh["score"] = [80, 80, 80] sh.close() # 重新打開(kāi)文件,修改鍵值對(duì) sh = shelve.open("shelve", writeback=True) sh["name"] = "芙蘭朵露" sh["score"].append(90) sh.close() # 再次重新打開(kāi)文件,查看鍵值對(duì) sh = shelve.open("shelve") print(sh["name"]) print(sh["score"]) """ 芙蘭朵露 [80, 80, 80, 90] """ sh.close()
可以看到都發(fā)生改變了,但這個(gè)參數(shù)會(huì)導(dǎo)致額外的內(nèi)存消耗。當(dāng)指定 writeback=True 的時(shí)候,shelve 會(huì)將讀取的對(duì)象都放到一個(gè)內(nèi)存緩存當(dāng)中。比如我們操作了 20 個(gè)持久化的對(duì)象,但只修改了一個(gè),剩余的 19 個(gè)只是查看并沒(méi)有做修改,但當(dāng) sh.close() 的時(shí)候,會(huì)將這 20 個(gè)對(duì)象都寫(xiě)回去。
因?yàn)?shelve 不知道你會(huì)對(duì)哪個(gè)對(duì)象做修改,所以不管你是查看還是修改,都會(huì)放到緩存當(dāng)中,然后再一次性都寫(xiě)回去。這樣就會(huì)造成兩點(diǎn)影響:
- shelve 會(huì)把我們使用的對(duì)象放到內(nèi)存的另一片空間中,等于是額外拷貝了一份。
- 雖然操作了 N 個(gè)對(duì)象,但只修改了 1 個(gè),而 shelve 會(huì)把 N 個(gè)對(duì)象都重新寫(xiě)回去,從而造成性能上的問(wèn)題,導(dǎo)致效率降低。
因此加不加這個(gè)參數(shù),由具體情況決定。
綜上所述,Python 算是自帶了小型數(shù)據(jù)庫(kù),看看能不能在合適的場(chǎng)景中把它用上。
到此這篇關(guān)于詳解Python中dbm模塊和shelve模塊的使用的文章就介紹到這了,更多相關(guān)Python dbm shelve內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python使用Marshmallow輕松實(shí)現(xiàn)序列化和反序列化
這篇文章主要為大家詳細(xì)介紹了Python如何使用Marshmallow輕松實(shí)現(xiàn)序列化和反序列化,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-03-03numpy中hstack vstack stack concatenate函數(shù)示例詳解
這篇文章主要為大家介紹了numpy中hstack vstack stack concatenate函數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Python字符串逆序的實(shí)現(xiàn)方法【一題多解】
今天小編就為大家分享一篇關(guān)于Python字符串逆序的實(shí)現(xiàn)方法【一題多解】,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02python通過(guò)wxPython打開(kāi)一個(gè)音頻文件并播放的方法
這篇文章主要介紹了python通過(guò)wxPython打開(kāi)一個(gè)音頻文件并播放的方法,實(shí)例分析了wxPython操作音頻文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03基于Python的Jenkins的二次開(kāi)發(fā)操作
這篇文章主要介紹了基于Python的Jenkins的二次開(kāi)發(fā)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05Python巧用SnowNLP實(shí)現(xiàn)生成srt字幕文件
SnowNLP是一個(gè)可以方便的處理中文文本內(nèi)容的python類(lèi)庫(kù),本文主要為大家詳細(xì)介紹了Python如何巧用SnowNLP實(shí)現(xiàn)將一段話一鍵生成srt字幕文件,感興趣的可以了解下2024-01-01Python中的logging模塊實(shí)現(xiàn)日志打印
這篇文章主要介紹了Python中的logging模塊實(shí)現(xiàn)日志打印,其實(shí)不止print打印日志方便排查問(wèn)題,Python自帶的logging模塊,也可以很簡(jiǎn)單就能實(shí)現(xiàn)日志的配置和打印,下面來(lái)看看具體的實(shí)現(xiàn)過(guò)程吧,需要的朋友可以參考一下2022-03-03三個(gè)python爬蟲(chóng)項(xiàng)目實(shí)例代碼
這篇文章主要介紹了三個(gè)python爬蟲(chóng)項(xiàng)目實(shí)例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12