1. 程式人生 > >python--資料持久化

python--資料持久化

python中與資料持久化有關的模組有很多,像pickle、json之類的就不介紹了,這裡介紹兩個其他的模組:dbm和shelve

1.dbm

'''
在一些小型程式中,不需要關係型資料庫時,可以方便的用持久字典來儲存鍵值對,和python中的字典非常類似。而且dbm的鍵和值都必須是str或者bytes型別
'''
import dbm

'''
這裡第一個引數直接傳入檔名,第二個引數表示模式
常見的模式:
r:可讀,預設就是這個模式
w:可讀可寫
但是r、w,都必須確保檔案已經存在,否則報錯。

c:可讀可寫,檔案不存在時會建立
n:可讀可寫,但總是會建立一個新的檔案,也就是說如果建立同名檔案,那麼之前的內容都會被清空,也就是起不到追加的效果。

因此我們平常的模式一般都會選擇c


第三個引數是許可權,這個在windows下基本不用,是一組用八進位制表示的數字,預設是0o666,都是可讀可寫不可執行
'''
db = dbm.open("store", "c")

# 開啟檔案之後,就可以儲存值了
# 注意key和value都必須是str或者bytes型別
db["name"] = "satori"
db["age"] = "16"
db["gender"] = "f"
db["anime"] = "東方地靈殿"

# 關閉檔案,將內容寫到磁碟上
db.close()


################################################################
# 開啟檔案
db = dbm.open("store", "c")
print(db.keys())  # [b'name', b'age', b'gender', b'anime']
for key in db.keys():
    print(f"key={key}, value={db[key]}")
    '''
    key=b'name', value=b'satori'
    key=b'age', value=b'16'
    key=b'gender', value=b'f'
    key=b'anime', value=b'\xe4\xb8\x9c\xe6\x96\xb9\xe5\x9c\xb0\xe7\x81\xb5\xe6\xae\xbf'
    '''

  會多出這麼三個檔案

 

2.shelve

'''

shelve和dbm比較類似,但是功能遠比dbm強大,因為它可以持久化任意物件
'''
import shelve

# 引數flag預設是c,因此我們只需要傳入檔名就可以了,這個是自動追加在後面的
# 也就是說我寫完之後,再次開啟繼續寫的話,只會追加不會清空
sh = shelve.open("shelve")

sh["dict"] = {"name": "satori", "age": 16}
sh["list"] = [1, 2, 3, 4]
sh["set"] = {1, 2, 3, 2}

# 寫完之後關閉檔案,刷到記憶體裡面
# 關閉之後就無法操作了
sh.close()


# 下面我們就可以操作資料了,下面的程式碼即便寫在另一個py檔案裡面也是可以的
sh2 = shelve.open("shelve")
print(sh2["dict"], sh2["dict"].keys())  # {'name': 'satori', 'age': 16} dict_keys(['name', 'age'])
print(sh2["list"], sum(sh2["list"]))  # [1, 2, 3, 4] 10
print(sh2["set"])  # {1, 2, 3}
sh2.close()


# 可以看到,拿出來的就是原生的物件,可以直接用來進行操作的。那我們看看自己定義的類可不可以呢?
sh3 = shelve.open("shelve")


class A:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @property
    def print_info(self):
        return f"my name is {self.name}, age is {self.age}"


a = A("satori", 16)
# 將這個類和類的一個例項物件儲存進去
sh3["A"] = A
sh3["a"] = a
sh3.close()


######################################
sh4 = shelve.open("shelve")

# sh4["A"]拿到A這個類,傳入引數,呼叫方法
print(sh4["A"]("mashiro", "17").print_info)  # my name is mashiro, age is 17

# sh4["a"]拿到a這個例項物件,直接呼叫方法
print(sh4["a"].print_info)  # my name is satori, age is 16

# 我們發現依舊是可以的,說明了shelve這個模組真的很強大

  

# 我們再來看一個例子
import shelve

sh = shelve.open("shelve")
sh["list"] = [1, 2, 3]
sh["str"] = "mashiro"
sh.close()

##############################
sh = shelve.open("shelve")
sh["list"].append("xxxx")
sh["str"] = "satori"
sh.close()

#######################
sh = shelve.open("shelve")
print(sh["list"])  # [1, 2, 3]
print(sh["str"])  # satori
'''
分析結果,第一次開啟檔案我們建立兩個鍵值對
sh["list"] = [1, 2, 3]
sh["str"] = "mashiro"

第二次開啟檔案,修改了兩個鍵的值
第三次開啟檔案,列印。但是我們發現sh["str"]改變了,但是sh["list"]沒有改變,這是為什麼?
首先sh["str"] = "satori"很好理解,但是為什麼sh["list"]沒有變?
因為=,我們是直接賦值,將這一塊記憶體裡面的值給換掉,而sh["list"]我們是做append操作,這只是在原來的基礎上進行修改
shelve預設情況下是不會記錄,持久化物件的修改的,除非你是建立新的物件,或者是把原來的物件給換掉
如果是在原來的基礎上(可變型別),比如列表、字典,進行新增或者刪除操作,這些是不會被記錄的
所以:sh["list"]=[1, 2, 3]  sh["list"].append("xxxx")  --->sh["list"]仍是[1, 2, 3]不會是[1, 2, 3, "xxx"]
因為shelve沒有記錄物件自身的修改,如果我想得到期望的結果,一種方法是把物件整體換掉
sh["list"] = [1, 2, 3, "xxxx"],這樣等於是重新賦值,是可行的。但是有時候我們不知道列表裡面內容,或者列表裡面的內容是一些函式、類什麼的、不好寫的話,該咋辦呢?
其實我們在開啟檔案的時候,還可以加上一個引數,叫做writeback
'''

  

import shelve

sh = shelve.open("shelve")
sh["list"] = [1, 2, 3]
sh["str"] = "mashiro"
sh.close()

##############################
# 如果我們需要進行修改,那麼加上一個writeback=True就可以了,從名字也能看出來
# 這是會將修改的內容從新寫回去
sh = shelve.open("shelve", writeback=True)
sh["list"].append("xxxx")
sh["str"] = "satori"
sh.close()

#######################
sh = shelve.open("shelve")
print(sh["list"])  # [1, 2, 3, 'xxxx']
print(sh["str"])  # satori
'''
可以看到都發生改變了,但是這個引數有缺陷,就是會有額外的記憶體消耗。當我們加上writeback=True的時候shelve會將我們讀取的物件都放到一個記憶體快取當中。
比如說我們獲取了20持久化的物件,但是我們只修改了一個,剩餘的19個只是檢視並沒有做修改,但當我們sh.close()的時候,會將這20個物件都寫回去
因為shelve不知道你會對哪個物件進行修改,於是不管你是檢視還是修改,都會放到快取當中,然後再一次性都寫回去。
這樣會造成兩點:
1.物件放到記憶體快取當中,等於是重新拷貝了一份,因為我們讀取檔案已經到記憶體當中了,而shelve又把我們使用的物件放當記憶體的另一片空間中
2.寫入資料,我們明明只修改了一份資料,但是它把20份都重新寫回去了,這樣會造成效能上的問題,導致效率會降低。
因此加不加這個引數,由具體情況決定
'''