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