Python之路 - 序列化
先說(shuō)個(gè)例子 , 當(dāng)我們將一個(gè)字典或者列表再或者變量存入磁盤中 , 而存入磁盤后原本數(shù)據(jù)類型就得不到保持了 . 這個(gè)時(shí)候我們就得用序列化和反序列化了
序列化是將對(duì)象進(jìn)行存儲(chǔ)時(shí)保持當(dāng)時(shí)對(duì)象的狀態(tài) , 實(shí)現(xiàn)其生命周期的延長(zhǎng)
反序列化則是將存儲(chǔ)的對(duì)象讀取出來(lái)并轉(zhuǎn)成原本的數(shù)據(jù)類型
序列化的目的
- 以某種存儲(chǔ)形式使自定義對(duì)象持久化
- 將對(duì)象從一個(gè)地方傳遞到另一個(gè)地方
- 使程序更具維護(hù)性
此時(shí)應(yīng)該想到 eval :那么問(wèn)題來(lái)了 , 序列化所達(dá)到的功能我用eval()也能達(dá)到啊 , eval()直接就可以把字符串轉(zhuǎn)換成python解釋器能解釋的代碼 , 即可以直接將字符串中的字典 , 列表都轉(zhuǎn)成原來(lái)的數(shù)據(jù)類型 . 但是要注意的是 , eval本來(lái)就是將字符串內(nèi)容轉(zhuǎn)換成python可以執(zhí)行的代碼 , 并執(zhí)行它 , 這樣看來(lái)eval就不安全了 , 因?yàn)槿绻谖夷茏x取的內(nèi)容中含有一些其他的 ' 危險(xiǎn)代碼 ' 如 ' 刪除文件 ' , 于是造成了毀滅性的打擊 , 所以eval是存在風(fēng)險(xiǎn)的
Python為我們提供了三個(gè)序列化工具 , 分別是 json , pickle , shelve
json :four_leaf_clover:
用于字符串和python數(shù)據(jù)類型之間進(jìn)行轉(zhuǎn)換 , 因?yàn)閖son表示出來(lái)就是一個(gè)字符串
json模塊提供了四個(gè)方法
| 方法 | 描述 |
|---|---|
| dump | 接收一個(gè)文件句柄 , 將原數(shù)據(jù)類型轉(zhuǎn)換成字符串寫入文件 |
| load | 接收一個(gè)文件句柄 , 將文件中的字符串轉(zhuǎn)換成原數(shù)據(jù)類型返回 |
| dumps | 接收一個(gè)數(shù)據(jù)類型 , 將其轉(zhuǎn)換成字符串 |
| loads | 接收一個(gè)字符串 , 將其轉(zhuǎn)換成原數(shù)據(jù)類型 |
dump 和 load 實(shí)例
# 導(dǎo)入json模塊
import json
# 創(chuàng)建一個(gè)文件句柄
f = open('json_file','w')
# 創(chuàng)建一個(gè)字典
dic = {'k1':'v1','k2':'v2'}
# 將字典轉(zhuǎn)換成字符串寫入文件
json.dump(dic,f)
# 關(guān)閉文件
f.close()
# 創(chuàng)建一個(gè)文件句柄
f = open('json_file')
# 將文件中的字符串讀出并轉(zhuǎn)換成原數(shù)據(jù)類型
dic2 = json.load(f)
# 關(guān)閉文件句柄
f.close()
# 打印類型和結(jié)果
print(type(dic2),dic2)
# <class 'dict'> {'k1': 'v1', 'k2': 'v2'}
dumps 和 loads 實(shí)例
# 導(dǎo)入json模塊
import json
# 創(chuàng)建一個(gè)新列表
lst = ['1','2','3','4']
# 將列表轉(zhuǎn)換成字符串,用j_d來(lái)接收返回值
j_d = json.dumps(lst)
# 將字符串轉(zhuǎn)換成原數(shù)據(jù)類型,用j_s來(lái)接收返回值
j_s = json.loads(j_d)
# 打印j_d的值以及類型
print(j_d,type(j_d))
# ["1", "2", "3", "4"] <class 'str'>
# 打印j_s的值以及類型
print(j_s,type(j_s))
# ['1', '2', '3', '4'] <class 'list'>
loads的特殊情況
# 導(dǎo)入json模塊
import json
# 創(chuàng)建一個(gè)字符串,內(nèi)部為一個(gè)字典
dic_s = "{'k1':'v1','k2':'v2','k3':3}"
# 將字符串轉(zhuǎn)換成字典
json.loads(dic_s)
# 解釋器出現(xiàn)報(bào)錯(cuò)
# json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
'''
報(bào)錯(cuò)原因,用json的loads功能時(shí),字符串類型的字典中的字符串必須由 "" 表示
即上面的dic_s應(yīng)該改為 '{"k1":"v1","k2":"v2","k3":3}'
結(jié)論:用json的loads功能時(shí),字符串類型的字典中的字符串必須由 "" 表示
'''
PS : json可用于不同語(yǔ)言之間的數(shù)據(jù)交換
pickle :four_leaf_clover:
用于python特有的類型和python的數(shù)據(jù)類型間進(jìn)行轉(zhuǎn)換
pickle模塊也提供了四個(gè)方法 , 與json一樣 dumps , dump , loads , load
由于pickle是對(duì)于python特有的類型 , 所以 load 和 loads方法不僅支持字典 , 列表 , 它還能把python中任意的數(shù)據(jù)類型進(jìn)行序列化
-------dumps和loads--------
# 導(dǎo)入pickle模塊
import pickle
# 創(chuàng)建一個(gè)字典
dic = {'k1':'v1','k2':'v2'}
# 將字典轉(zhuǎn)換成二進(jìn)制內(nèi)容
p_d = pickle.dumps(dic)
# 將二進(jìn)制內(nèi)容轉(zhuǎn)換成字典
p_l = pickle.loads(p_d)
# 打印p_d
print(p_d)
# b'\x80\x03}q\x00(X\x02\x00\x00\x00k2q\x01X\x02\x00\x00\x00v2q\x02X\x02\x00\x00\x00k1q\x03X\x02\x00\x00\x00v1q\x04u.'
# 打印p_d的類型
print(type(p_d))
# <class 'bytes'>
# 打印p_l
print(p_l)
# {'k2': 'v2', 'k1': 'v1'}
# 打印p_l的類型
print(type(p_l))
# <class 'dict'>
---------dump 和 load---------
# 創(chuàng)建一個(gè)文件句柄
f = open('pickle_file','wb')
# 寫入內(nèi)容
pickle.dump('lyon',f)
# 關(guān)閉文件
f.close()
# 創(chuàng)建一個(gè)文件句柄
f = open('pickle_file','rb')
# 讀出內(nèi)容
p_f = pickle.load(f)
# 關(guān)閉文件
f.close()
# 打印
print(p_f)
# lyon
但是pickle僅僅只能對(duì)python中的數(shù)據(jù)進(jìn)行序列化 , 反序列化時(shí)其他語(yǔ)言就無(wú)法讀懂了這是什么了 , 所以我們一般用推薦使用json
shelve :four_leaf_clover:
shelve也是python提供給我們的序列化工具 , 比pickle用起來(lái)簡(jiǎn)單一些
shelve只提供給我們一個(gè)open方法 , 是用key來(lái)訪問(wèn)的 , 使用起來(lái)和字典類似
# 導(dǎo)入shelve模塊
import shelve
# shelve提供open方法
f = shelve.open('shelve_file')
# 直接對(duì)文件句柄進(jìn)行操作,就可以寫入文件中
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}
# 關(guān)閉文件
f.close()
# 打開(kāi)文件
f1 = shelve.open('shelve_file')
# 直接用key取值,key不存在就報(bào)錯(cuò)
existing = f1['key']
# 關(guān)閉文件
f1.close()
# 打印結(jié)果
print(existing)
# {'float': 9.5, 'int': 10, 'string': 'Sample data'}
shelve不支持多個(gè)應(yīng)用同時(shí)往一個(gè)數(shù)據(jù)庫(kù)進(jìn)行操作 , 所以當(dāng)我們知道我們的應(yīng)用如果只進(jìn)行操作 , 我們可以設(shè)置shelve.open() 方法的參數(shù)來(lái)進(jìn)行
shelve.open(filename, flag='c', protocol=None, writeback=False)
import shelve
# flag參數(shù)為設(shè)置操作模式,r 設(shè)置只讀模式
f = shelve.open('shelve_file', flag='r')
existing = f['key']
f.close()
print(existing)
writeback參數(shù) , 可以減少我們出錯(cuò)的概率 , 并且讓對(duì)象的持久化對(duì)用戶更加的透明了 ; 但這種方式并不是所有的情況下都需要 , 首先 , 使用writeback以后 , shelf在open()的時(shí)候會(huì)增加額外的內(nèi)存消耗 , 并且當(dāng)數(shù)據(jù)庫(kù)在close()的時(shí)候會(huì)將緩存中的每一個(gè)對(duì)象都寫入到數(shù)據(jù)庫(kù) , 這也會(huì)帶來(lái)額外的等待時(shí)間 , 因?yàn)閟helve沒(méi)有辦法知道緩存中哪些對(duì)象修改了 , 哪些對(duì)象沒(méi)有修改 , 因此所有的對(duì)象都會(huì)被寫入
import shelve
f1 = shelve.open('shelve_file')
print(f1['key'])
f1['key']['new_value'] = 'this was not here before'
f1.close()
# 設(shè)置writeback
f2 = shelve.open('shelve_file', writeback=True)
print(f2['key'])
f2['key']['new_value'] = 'this was not here before'
f2.close()