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}
相关文章
相关标签/搜索