Python 對象序列化——pickle and cPickle

Python 對象序列化——pickle and cPickle


從這篇文章粗略翻譯的pickle and cPicklepython

pickle模塊能夠實現任意的Python對象轉換爲一系列字節(即序列化對象)的算法。這些字節流能夠 被傳輸或存儲,接着也能夠重構爲一個和原先對象具備相同特徵的新對象。算法

cPickle模塊實現了一樣的算法,但它是用c而不是python。所以,它比python實現的快上好幾倍, 可是不容許使用者去繼承Pickle。若是繼承對於你的使用不是很重要,那麼你大可使用cPickle。數據庫

Woring: pickle的文檔明確的代表它不提供安全保證。因此慎用pickle來做爲內部進程通訊或者數
據存儲,也不要相信那些你不能驗證安全性的數據。安全


Importing


一般優先試用 cPickle,只有當 cPickle 沒法正常 import 的時候,採用 pickle 來替代。bash

try:
   import cPickle as pickle
except:
   import pickle

Encoding and Decoding Data in Strings


第一個示例是將數據結構編碼爲字符串,而後輸出到控制檯。例子中數據結構徹底由基本類型組成。pickle 能夠編碼任意類的實例,就像下面栗子中演示的那樣:用 pickle.dumps() 來建立對象的字符串表示。數據結構

try:
    import cPickle as pickle
except:
    import pickle
import pprint

data = [ { 'a':'A', 'b':2, 'c':3.0 } ]
print 'DATA:',
pprint.pprint(data)

data_string = pickle.dumps(data)
print 'PICKLE:', data_string

pickle 默認試用 ASSCII 字符串來進行編碼解碼。也支持效率更高的二進制格式,可是下面的例子爲了方便閱讀,仍是使用了 ASSCII 碼。app

$ python pickle_string.py

DATA:[{'a': 'A', 'b': 2, 'c': 3.0}]
PICKLE: (lp1
(dp2
S'a'
S'A'
sS'c'
F3
sS'b'
I2
sa.

數據被序列化以後,你就能夠將他寫入文件、socket、pipe、etc.而後你能夠讀取文件並unpickle 這些數據來構造一個新的對象。socket


try: import cPickle as pickle except: import pickle import pprint data1 = [ { 'a':'A', 'b':2, 'c':3.0 } ] print 'BEFORE:', pprint.pprint(data1) data1_string = pickle.dumps(data1) data2 = pickle.loads(data1_string) print 'AFTER:', pprint.pprint(data2) print 'SAME?:', (data1 is data2) print 'EQUAL?:', (data1 == data2)

如同例子中演示的那樣,新的對象與以前的對象相等,可是並非同一個對象。編碼


$ python pickle_unpickle.py BEFORE:[{'a': 'A', 'b': 2, 'c': 3.0}] AFTER:[{'a': 'A', 'b': 2, 'c': 3.0}] SAME?: False EQUAL?: True

Working with Streams


除了 dumps() 跟 loads(), pickle 還有其餘比較方便的方法來操做類文件流。能夠同時寫入多個對象到一個 stream 中,而後對象數量與大小的時候從 stream 讀取他們。命令行


try: import cPickle as pickle except: import pickle import pprint from StringIO import StringIO class SimpleObject(object): def __init__(self, name): self.name = name l = list(name) l.reverse() self.name_backwards = ''.join(l) return data = [] data.append(SimpleObject('pickle')) data.append(SimpleObject('cPickle')) data.append(SimpleObject('last')) # Simulate a file with StringIO out_s = StringIO() # Write to the stream for o in data: print 'WRITING: %s (%s)' % (o.name, o.name_backwards) pickle.dump(o, out_s) out_s.flush() # Set up a read-able stream in_s = StringIO(out_s.getvalue()) # Read the data while True: try: o = pickle.load(in_s) except EOFError: break else: print 'READ: %s (%s)' % (o.name, o.name_backwards)

上面例子中使用了 StringIO 緩衝區來模擬streams,這樣咱們在創建可讀流的時候能夠玩一些技巧。一些接單的數據庫格式也可使用 pickles 來存儲數據,固然,若是試用 shelve 來存儲會更加簡單。

$ python pickle_stream.py

WRITING: pickle (elkcip)
WRITING: cPickle (elkciPc)
WRITING: last (tsal)
READ: pickle (elkcip)
READ: cPickle (elkciPc)
READ: last (tsal)

除了存儲數據,pickles 用來作內部通訊的機會也不少。舉個例子:用 os.fork() 和 os.pipe() 能夠創建一個工做進程,而後這個進程會從管道中讀取數據並把結果傳遞給另一個管道。由於這些代碼是用來管理worker pool 跟 發送任務跟接受任務的,沒有什麼特殊的內容,因此這些核心代碼能夠被拿來重複利用。若是你在試用 pipe 或者 sockets,那麼在 dumping完對象以後,不要忘記刷新它們並經過其間的鏈接將數據推送到另一個進程。若是你不想本身寫 worker pool manager 的話,能夠看一下multiprocessing

Problems Reconstructing Objects


須要注意的是,在序列化實例的時候,咱們只是對於數據來進行序列化,而沒法對類的定義進行序列化。
下面的栗子:

try:
    import cPickle as pickle
except:
    import pickle
import sys

class SimpleObject(object):

    def __init__(self, name):
        self.name = name
        l = list(name)
        l.reverse()
        self.name_backwards = ''.join(l)
        return

if __name__ == '__main__':
    data = []
    data.append(SimpleObject('pickle'))
    data.append(SimpleObject('cPickle'))
    data.append(SimpleObject('last'))

    try:
        filename = sys.argv[1]
    except IndexError:
        raise RuntimeError('Please specify a filename as an argument to %s' % sys.argv[0])

    out_s = open(filename, 'wb')
    try:
        # Write to the stream
        for o in data:
            print 'WRITING: %s (%s)' % (o.name, o.name_backwards)
            pickle.dump(o, out_s)
    finally:
        out_s.close()

運行的時候,這個腳本會以命令行中給出的參數建立一個文件。

$ python pickle_dump_to_file_1.py test.dat

WRITING: pickle (elkcip)
WRITING: cPickle (elkciPc)
WRITING: last (tsal)

下面是一個會報錯的栗子:

try:
    import cPickle as pickle
except:
    import pickle
import pprint
from StringIO import StringIO
import sys


try:
    filename = sys.argv[1]
except IndexError:
    raise RuntimeError('Please specify a filename as an argument to %s' % sys.argv[0])

in_s = open(filename, 'rb')
try:
    # Read the data
    while True:
        try:
            o = pickle.load(in_s)
        except EOFError:
            break
        else:
            print 'READ: %s (%s)' % (o.name, o.name_backwards)
finally:
    in_s.close()

這個報錯是由於沒有SimpleObject類。

$ python pickle_load_from_file_1.py test.dat

Traceback (most recent call last):
  File "pickle_load_from_file_1.py", line 52, in <module>
    o = pickle.load(in_s)
AttributeError: 'module' object has no attribute 'SimpleObject'

咱們經過從原來的腳本中import SimpleObject來修正上面的錯誤。
在上面文件中的增長下面一行:

from pickle_dump_to_file_1 import SimpleObject
$ python pickle_load_from_file_2.py test.dat

READ: pickle (elkcip)
READ: cPickle (elkciPc)
READ: last (tsal)

sockets, file handles, database connections ...這些數據類型是沒法被序列化的,咱們在處理相似數據類型的時候不得不特殊處理。而利用pickle protocol 則能夠控制序列化的細節。

class Data(object):
    def __init__(self, x, y):
        self._x = x
        self._y = y

    def __getstate__(self):
        d = self.__dict__.copy()
        del d["_y"]
        return d
    def __setstate__(self, state):
        self.__dict__.update(state)
d = Data(10, 20)
s = cPickle.dumps(d, 2)
d2 = cPickle.loads(s)
##d2.__dict__
##{'_x': 10}
相關文章
相關標籤/搜索