# Python 使用pickle/cPickle模塊進行數據的序列化 """Python序列化的概念很簡單。內存里面有一個數據結構, 你希望將它保存下來,重用,或者發送給其他人。你會怎么做? 這取決於你想要怎么保存,怎么重用,發送給誰。很多游戲允許你在退出的時候保存進度, 然后你再次啟動的時候回到上次退出的地方。(實際上,很多非游戲程序也會這么干)在這種情況下, 一個捕獲了當前進度的數據結構需要在你退出的時候保存到硬盤上,接着在你重新啟動的時候從硬盤上加載進來。 """ """Python標准庫提供pickle和cPickle模塊。cPickle是用C編碼的,在運行效率上比pickle要高, 但是cPickle模塊中定義的類型不能被繼承(其實大多數時候,我們不需要從這些類型中繼承,推薦使用cPickle) cPickle和pickle的序列化/反序列化規則是一樣的,使用pickle序列化一個對象,可以使用cPickle來反序列化。 同時,這兩個模塊在處理自引用類型時會變得更加“聰明”,它不會無限制的遞歸序列化自引用對象, 對於同一對象的多次引用,它只會序列化一次。 """ """ pickle模塊中的兩個主要函數是dump()和load()。dump()函數接受一個數據對象和一個文件句柄作為參數, 把數據對象以特定的格式保存到給定的文件中。當我們使用load()函數從文件中取出已保存的對象時, pickle知道如何恢復這些對象到它們本來的格式。 dumps()函數執行和dump()函數相同的序列化。取代接受流對象並將序列化后的數據保存到磁盤文件,這個函數簡單的返回序列化的數據。 loads()函數執行和load()函數一樣的反序列化。取代接受一個流對象並去文件讀取序列化后的數據,它接受包含序列化后的數據的str對象, 直接返回的對象。 """ # pickle.dump(obj, file[, protocol]), 將對象進行序列化 """這是將對象持久化的方法,參數的含義分別為: obj: 要持久化保存的對象,可以是一個python列表,字典,元組等; file: 一個擁有 write() 方法的對象,並且這個 write() 方法能接收一個字符串作為參數。 這個對象可以是一個以寫模式打開的文件對象或者一個 StringIO 對象,或者其他自定義的滿足條件的對象。 protocol: 這是一個可選的參數,默認為 0 ,如果設置為 1 或 True, 則以高壓縮的二進制格式保存持久化后的對象,否則以ASCII格式保存。 """ # 將序列化的對象進行還原,pickle.load(file) """ 只有一個參數 file ,對應於上面 dump 方法中的 file 參數。 這個 file 必須是一個擁有一個能接收一個整數為參數的 read() 方法以及一個不接收任何參數的 readline() 方法, 並且這兩個方法的返回值都應該是字符串。這可以是一個打開為讀的文件對象、StringIO 對象或其他任何滿足條件的對象。 """ # 示例 import pickle # 也可以這樣: # import cPickle as pickle # python3 中已經移除了cPickle,直接使用pickle即可 obj = {"a": 1, "b": 2, "c": 3} f = open("./tmp.txt", "wb") # 將 obj 持久化保存到文件 tmp.txt 中 pickle.dump(obj, f) del obj f.close() f2 = open("./tmp.txt", "rb") # 從 tmp.txt 中讀取並恢復 obj 對象 obj2 = pickle.load(f2) f2.close() print(obj2) """ 不過實際應用中,如果是python2開發環境,我們可能還會有一些改進,比如用 cPickle 來代替 pickle(python3移除了cPickle, 直接使用pickle即可) , 前者是后者的一個 C 語言實現版本,擁有更快的速度。 另外,有時在 dump 時也會將第三個參數設為 True 以提高壓縮比。再來看下面的例子: """
def test_cPickle(): # import cPickle as pickle import pickle import random import os import time LENGTH = 1024 * 10240 d = {} a = [] for i in range(LENGTH): a.append(random.randint(0, 255)) d["a"] = a print("dumping...") t1 = time.time() pickle.dump(d, open("tmp1.dat", "wb"), True) print("dump1: %.3fs" % (time.time() - t1)) t1 = time.time() pickle.dump(d, open("tmp2.dat", "w")) print("dump2: %.3fs" % (time.time() - t1)) s1 = os.stat("tmp1.dat").st_size s2 = os.stat("tmp2.dat").st_size print("%d, %d, %.2f%%" % (s1, s2, 100.0 * s1 / s2)) print("loading...") t1 = time.time() obj1 = pickle.load(open("tmp1.dat", "rb")) print("load1: %.3fs" % (time.time() - t1)) t1 = time.time() obj2 = pickle.load(open("tmp2.dat", "r")) print("load2: %.3fs" % (time.time() - t1)) test_cPickle()
""" 可以看到,dump 時如果指定了 protocol 為 True,壓縮過后的文件的大小只有原來的文件的 30% , 同時無論在 dump 時還是 load 時所耗費的時間都比原來少。因此,一般來說,可以建議把這個值設為 True 。 另外,pickle 模塊還提供 dumps 和 loads 兩個方法,用法與上面的 dump 和 load 方法類似, 只是不需要輸入 file 參數,輸入及輸出都是字符串對象,有些場景中使用這兩個方法可能更為方便。 """ def test_dumps_loads(): import pickle my_list = [1, 2, 3, 4, 5] item = pickle.dumps(my_list, protocol=True) print(item) with open("./test.txt", "wb") as f: f.write(item) with open("./test.txt", "rb") as f: my_byte = f.read() item2 = pickle.loads(my_byte) print(item2) test_dumps_loads()