Python 对象序列化——pickle and cPickle

Python 对象序列化——pickle and cPickle


从这篇文章粗略翻译的pickle and cPickle

pickle模块可以实现任意的Python对象转换为一系列字节(即序列化对象)的算法。这些字节流可以 被传输或存储,接着也可以重构为一个和原先对象具有相同特征的新对象。

cPickle模块实现了同样的算法,但它是用c而不是python。因此,它比python实现的快上好几倍, 但是不允许使用者去继承Pickle。如果继承对于你的使用不是很重要,那么你大可以使用cPickle。

Woring: pickle的文档明确的表明它不提供安全保证。所以慎用pickle来作为内部进程通信或者数
据存储,也不要相信那些你不能验证安全性的数据。


Importing


通常优先试用 cPickle,只有当 cPickle 无法正常 import 的时候,采用 pickle 来替代。

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 码。

$ 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 这些数据来构造一个新的对象。


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 
    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}

你可能感兴趣的:(python,pickle,cpickle)