通过下面的代码可以简单了解pickle的用法和功能。
In [2]: import pickle
In [3]: class A:
pass
In [4]: a = A()
In [5]: a.foo = 'hello'
In [6]: a.bar = 2
In [7]: pick_ed = pickle.dumps(a)
In [8]: pick_ed
Out[8]: b'x80x03c__main__
A
qx00)x81qx01}qx02(Xx03x00x00x00fooqx03Xx05x00x00x00helloqx04Xx03x00x00x00barqx05Kx02ub.'
In [9]: unpick = pickle.loads(pick_ed)
In [10]: unpick
Out[10]: <__main__.A at 0x10ae67278>
In [11]: a
Out[11]: <__main__.A at 0x10ae67128>
In [12]: dir(unpick)
Out[12]:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slotnames__',
'__str__',
'__subclasshook__',
'__weakref__',
'bar',
'foo']
In [13]: unpick.foo
Out[13]: 'hello'
In [14]: unpick.bar
Out[14]: 2
可以看到pickle的用法和json有点像,但是有几点根本区别:
json是跨语言的通用的一种数据交换格式,一般用文本表示,人类可读。pickle是用来序列化Python对象,只是针对Python的,序列化的结果是二进制数据,人类不可读。而且json默认只可以序列化一部分的内置类型,pickle可以序列化相当多的数据。
另外还有一个古老的marshal也是内置的。但这个库主要是针对.pyc文件的。不支持自定义的类型,也不完善。比如不能处理循环应用,如果有一个对象引用了自己,那么用marshal的话Python解释器就挂了。
版本兼容问题
由于pickle是针对Python的,Python有不同的版本(并且2与3之间差异非常大),所以就要考虑到序列化出来的对象能不能被更高(或低?)版本的Python反序列化出来。
目前一共有5个pickle的协议版本,版本越高对应Pyhton的版本越高,0-2针对Python2,3-4针对Python3.
Protocol version 0 is the original “human-readable” protocol and is backwards compatible with earlier versions of Python.Protocol version 1 is an old binary format which is also compatible with earlier versions of Python.Protocol version 2 was introduced in Python 2.3. It provides much more efficient pickling of new-style classes. Refer to PEP 307for information about improvements brought by protocol 2. (从这个版本往后,性能有显著提高)Protocol version 3 was added in Python 3.0. It has explicit support for bytes objects and cannot be unpickled by Python 2.x.This is the default protocol, and the recommended protocol when compatibility with other Python 3 versions is required.Protocol version 4 was added in Python 3.4. It adds support for very large objects, pickling more kinds of objects, and some data format optimizations. Refer to PEP 3154 for information about improvements brought byprotocol 4.
pickle的大多数入口函数(例如dump(),dumps(),Pickler构造器)都接受一个协议版本的参数,其中内置了两个变量:
pickle.HIGHEST_PROTOCOL目前是4
pickle.DEFAULT_PROTOCOL目前是3
用法
和内置的json模块接口类似,dumps()用于返回序列化结果,dump()用于序列化然后写入文件。同理也有load()和loads()。其中,序列化dump(s)的时候可以指定协议的版本,反序列化的时候就不用了,会自动识别版本。这个和zip命令很像。
内置类型的序列化
大多数内置的类型都支持序列化和反序列化。需要特殊注意的是函数。函数的序列化只是取其名字和所在的module。函数的代码和属性(Python的函数是第一等对象,可以有属性)都不会被序列化。这就要求函数所在的模块在unpickle的环境中必须是可以import的,否则会出现ImportError或AttributeError。
这里有个地方很有意思:所有的lambda函数都是不可Pickle的。因为它们的名字都叫做
自定义类型的序列化
如同本文开头的实验代码一样,对我们自定义的对象在大部分情况下都不需要额外的操作就可以实现序列化/反序列化的操作。需要注意的是,在反序列化的过程中,并不是调用class的__init__()来初始化出一个对象,而是新建出一个未被初始化的实例,然后恢复它的属性(非常巧妙)。伪代码如下:
def save(obj):
return (obj.__class__, obj.__dict__)
def load(cls, attributes):
obj = cls.__new__(cls)
obj.__dict__.update(attributes)
return obj
如果希望在序列化的过程中做一些额外操作,例如保存对象的状态,可以使用pickle协议的魔术方法,**常见的是__setstate__()和__getstate__()。
安全问题(!)
pickle文档的开头就说:千万不要unpickle一个未知



