1. 程式人生 > 程式設計 >序列化Python物件的方法

序列化Python物件的方法

問題

你需要將一個Python物件序列化為一個位元組流,以便將它儲存到一個檔案、儲存到資料庫或者通過網路傳輸它。

解決方案

對於序列化最普遍的做法就是使用 pickle 模組。為了將一個物件儲存到一個檔案中,可以這樣做:

import pickle

data = ... # Some Python object
f = open('somefile','wb')
pickle.dump(data,f)

為了將一個物件轉儲為一個字串,可以使用 pickle.dumps()

s = pickle.dumps(data)

為了從位元組流中恢復一個物件,使用 pickle.load()

pickle.loads() 函式。比如:

# Restore from a file
f = open('somefile','rb')
data = pickle.load(f)

# Restore from a string
data = pickle.loads(s)

討論

對於大多數應用程式來講,dump() load() 函式的使用就是你有效使用 pickle 模組所需的全部了。 它可適用於絕大部分Python資料型別和使用者自定義類的物件例項。 如果你碰到某個庫可以讓你在資料庫中儲存/恢復Python物件或者是通過網路傳輸物件的話, 那麼很有可能這個庫的底層就使用了 pickle

模組。

pickle 是一種Python特有的自描述的資料編碼。 通過自描述,被序列化後的資料包含每個物件開始和結束以及它的型別資訊。 因此,你無需擔心物件記錄的定義,它總是能工作。 舉個例子,如果要處理多個物件,你可以這樣做:

>>> import pickle
>>> f = open('somedata','wb')
>>> pickle.dump([1,2,3,4],f)
>>> pickle.dump('hello',f)
>>> pickle.dump({'Apple','Pear','Banana'},f)
>>> f.close()
>>> f = open('somedata','rb')
>>> pickle.load(f)
[1,4]
>>> pickle.load(f)
'hello'
>>> pickle.load(f)
{'Apple','Banana'}
>>>

你還能序列化函式,類,還有介面,但是結果資料僅僅將它們的名稱編碼成對應的程式碼物件。例如:

>>> import math
>>> import pickle.
>>> pickle.dumps(math.cos)
b'\x80\x03cmath\ncos\nq\x00.'
>>>

當資料反序列化回來的時候,會先假定所有的源資料時可用的。 模組、類和函式會自動按需匯入進來。對於Python資料被不同機器上的解析器所共享的應用程式而言, 資料的儲存可能會有問題,因為所有的機器都必須訪問同一個原始碼。

千萬不要對不信任的資料使用pickle.load()。
pickle在載入時有一個副作用就是它會自動載入相應模組並構造例項物件。
但是某個壞人如果知道pickle的工作原理,
他就可以建立一個惡意的資料導致Python執行隨意指定的系統命令。
因此,一定要保證pickle只在相互之間可以認證對方的解析器的內部使用。

有些型別的物件是不能被序列化的。這些通常是那些依賴外部系統狀態的物件, 比如開啟的檔案,網路連線,執行緒,程序,棧幀等等。 使用者自定義類可以通過提供 __getstate__() __setstate__() 方法來繞過這些限制。 如果定義了這兩個方法,pickle.dump() 就會呼叫 __getstate__() 獲取序列化的物件。 類似的,__setstate__() 在反序列化時被呼叫。為了演示這個工作原理, 下面是一個在內部定義了一個執行緒但仍然可以序列化和反序列化的類:

# countdown.py
import time
import threading

class Countdown:
  def __init__(self,n):
    self.n = n
    self.thr = threading.Thread(target=self.run)
    self.thr.daemon = True
    self.thr.start()

  def run(self):
    while self.n > 0:
      print('T-minus',self.n)
      self.n -= 1
      time.sleep(5)

  def __getstate__(self):
    return self.n

  def __setstate__(self,n):
    self.__init__(n)

試著執行下面的序列化試驗程式碼:

>>> import countdown
>>> c = countdown.Countdown(30)
>>> T-minus 30
T-minus 29
T-minus 28
...

>>> # After a few moments
>>> f = open('cstate.p','wb')
>>> import pickle
>>> pickle.dump(c,f)
>>> f.close()

然後退出Python解析器並重啟後再試驗下:

>>> f = open('cstate.p','rb')
>>> pickle.load(f)
countdown.Countdown object at 0x10069e2d0>
T-minus 19
T-minus 18
...

你可以看到執行緒又奇蹟般的重生了,從你第一次序列化它的地方又恢復過來。

pickle 對於大型的資料結構比如使用 arraynumpy 模組建立的二進位制陣列效率並不是一個高效的編碼方式。 如果你需要移動大量的陣列資料,你最好是先在一個檔案中將其儲存為陣列資料塊或使用更高階的標準編碼方式如HDF5 (需要第三方庫的支援)。

由於 pickle 是Python特有的並且附著在原始碼上,所有如果需要長期儲存資料的時候不應該選用它。 例如,如果原始碼變動了,你所有的儲存資料可能會被破壞並且變得不可讀取。 坦白來講,對於在資料庫和存檔檔案中儲存資料時,你最好使用更加標準的資料編碼格式如XML,CSV或JSON。 這些編碼格式更標準,可以被不同的語言支援,並且也能很好的適應原始碼變更。

最後一點要注意的是 pickle 有大量的配置選項和一些棘手的問題。 對於最常見的使用場景,你不需要去擔心這個,但是如果你要在一個重要的程式中使用pickle去做序列化的話, 最好去查閱一下 官方文件 。

以上就是序列化Python物件的方法的詳細內容,更多關於序列化Python物件的資料請關注我們其它相關文章!