本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理
本文章来自腾讯云 作者:Python知识大全
想要学习Python?有问题得不到第一时间解决?来看看这里“1039649593”满足你的需求,资料都已经上传至文件中,可以自行下载!还有海量最新2020python学习资料。
点击查看
第一个示例是使用 dumps() 将一个数据结构编码为一个字符串,然后将其输出到控制台。它使用内置类型组成的数据结构,其实任何类的实例都可以被序列化,如后面的例子所示。
import pickle import pprint data = [{'a': 'A', 'b': 2, 'c': 3.0}] print('DATA:', end=' ') pprint.pprint(data) data_string = pickle.dumps(data) print('PICKLE: {!r}'.format(data_string))
默认情况下,Python 3 的序列化以兼容的二进制形式进行。
$ python3 pickle_string.py DATA: [{'a': 'A', 'b': 2, 'c': 3.0}] PICKLE: b'\x80\x03]q\x00}q\x01(X\x01\x00\x00\x00cq\x02G@\x08\x00 \x00\x00\x00\x00\x00X\x01\x00\x00\x00bq\x03K\x02X\x01\x00\x00\x0 0aq\x04X\x01\x00\x00\x00Aq\x05ua.'
一旦数据被序列化,你就可以把它写入到文件、socket、管道等等中。之后你可以读取这个文件,反序列化这些数据来构造具有相同值的新对象。
import pickle import pprint data1 = [{'a': 'A', 'b': 2, 'c': 3.0}] print('BEFORE: ', end=' ') pprint.pprint(data1) data1_string = pickle.dumps(data1) data2 = pickle.loads(data1_string) print('AFTER : ', end=' ') pprint.pprint(data2) print('SAME? :', (data1 is data2)) print('EQUAL?:', (data1 == data2))
新对象和之前的对象相等,但不是之前的对象。
$ python3 pickle_unpickle.py BEFORE: [{'a': 'A', 'b': 2, 'c': 3.0}] AFTER : [{'a': 'A', 'b': 2, 'c': 3.0}] SAME? : False EQUAL?: True
pickle 除了提供 dumps() 和 loads() ,还提供了非常方便的函数用于操作文件流。支持同时写多个对象到同一个流中,然后在不知道有多少个对象或不知道它们有多大时,能够从这个流中读取到这些对象。
pickle_stream.py
import io import pickle import pprint class SimpleObject: def __init__(self, name): self.name = name self.name_backwards = name[::-1] return data = [] data.append(SimpleObject('pickle')) data.append(SimpleObject('preserve')) data.append(SimpleObject('last')) # 模拟一个文件 out_s = io.BytesIO() # 写入流中 for o in data: print('WRITING : {} ({})'.format(o.name, o.name_backwards)) pickle.dump(o, out_s) out_s.flush() # 设置一个可读取的流 in_s = io.BytesIO(out_s.getvalue()) # 读取数据 while True: try: o = pickle.load(in_s) except EOFError: break else: print('READ : {} ({})'.format( o.name, o.name_backwards))
这个例子使用两个 BytesIO 缓冲区来模拟流。一个接收序列化对象,另一个通过 load() 方法读取第一个的值。一个简单的数据库格式也可以使用序列化来存储对象。 shelve 模块就是这样使用的一个范例。
$ python3 pickle_stream.py
WRITING : pickle (elkcip)
WRITING : preserve (evreserp)
WRITING : last (tsal)
READ : pickle (elkcip)
READ : preserve (evreserp)
READ : last (tsal)
除了用于存储数据,序列化在用于内部进程通信时也是非常灵活的。比如,使用 os.fork() 和 os.pipe() ,可以建立一些工作进程,它们从一个管道中读取任务说明并把结果输出到另一个管道。操作这些工作池、发送任务和接受返回的核心代码可以复用,因为任务和返回对象不是一个特殊的类。如果使用管道或者套接字,就不要忘记在序列化每个对象后刷新它们,并通过它们之间的连接将数据推送到另外一端。查看 multiprocessing 模块构建一个可复用的任务池管理器。
在处理自定义类时,你应该保证这些被序列化的类会在进程命名空间出现 只有数据实例才能被序列化,而不能是定义的类。在反序列化时,类的名字被用于寻找构造器以便创建新对象。接下来这个例子,是将一个类实例写入到文件中。
pickle_dump_to_file_1.py import pickle import sys class SimpleObject: def __init__(self, name): self.name = name l = list(name) l.reverse() self.name_backwards = ''.join(l) if __name__ == '__main__': data = [] data.append(SimpleObject('pickle')) data.append(SimpleObject('preserve')) data.append(SimpleObject('last')) filename = sys.argv[1] with open(filename, 'wb') as out_s: for o in data: print('WRITING: {} ({})'.format( o.name, o.name_backwards)) pickle.dump(o, out_s)
当我运行这个脚本时,它会创建名为我在命令行中输入的参数的文件。
$ python3 pickle_dump_to_file_1.py test.dat
WRITING: pickle (elkcip)
WRITING: preserve (evreserp)
WRITING: last (tsal)
之后尝试将刚才的序列化的结果对象装载进来是失败的。
pickle_load_from_file_1.py import pickle import pprint import sys filename = sys.argv[1] with open(filename, 'rb') as in_s: while True: try: o = pickle.load(in_s) except EOFError: break else: print('READ: {} ({})'.format( o.name, o.name_backwards))
这个版本失败了,因为这里没有可用的 SimpleObject 类。
$ python3 pickle_load_from_file_1.py test.dat Traceback (most recent call last): File "pickle_load_from_file_1.py", line 15, in <module> o = pickle.load(in_s) AttributeError: Can't get attribute 'SimpleObject' on <module '_ _main__' from 'pickle_load_from_file_1.py'>
下面是正确的版本,它从一开始的脚本中导入了 SimpleObject 类。添加导入语句可以让该脚本找到类并构建对象。
from pickle_dump_to_file_1 import SimpleObject
现在运行修改后的脚本可以得到预期的结果了。
$ python3 pickle_load_from_file_2.py test.dat
READ: pickle (elkcip)
READ: preserve (evreserp)
READ: last (tsal)

import pickle class State: def __init__(self, name): self.name = name def __repr__(self): return 'State({!r})'.format(self.__dict__) class MyClass: def __init__(self, name): print('MyClass.__init__({})'.format(name)) self._set_name(name) def _set_name(self, name): self.name = name self.computed = name[::-1] def __repr__(self): return 'MyClass({!r}) (computed={!r})'.format( self.name, self.computed) def __getstate__(self): state = State(self.name) print('__getstate__ -> {!r}'.format(state)) return state def __setstate__(self, state): print('__setstate__({!r})'.format(state)) self._set_name(state.name) inst = MyClass('name here') print('Before:', inst) dumped = pickle.dumps(inst) reloaded = pickle.loads(dumped) print('After:', reloaded)
循环引用
序列化协议会自动处理对象间的循环引用,所以即使复杂的数据结构也不需要去特殊处理。考虑下图,它包含了多个循环,但正确的结构仍然能被反序列化输出。
序列化一个循环引用的数据结构
pickle_cycle.py
import pickle class Node: """一个简单的有向图 """ def __init__(self, name): self.name = name self.connections = [] def add_edge(self, node): """在这个节点和其他节点间建立一条边 """ self.connections.append(node) def __iter__(self): return iter(self.connections) def preorder_traversal(root, seen=None, parent=None): """给一个图生成边的生成器函数 """ if seen is None: seen = set() yield (parent, root) if root in seen: return seen.add(root) for node in root: recurse = preorder_traversal(node, seen, root) for parent, subnode in recurse: yield (parent, subnode) def show_edges(root): """打印输出图的所有边 """ for parent, child in preorder_traversal(root): if not parent: continue print('{:>5} -> {:>2} ({})'.format( parent.name, child.name, id(child))) # 创建有向图 root = Node('root') a = Node('a') b = Node('b') c = Node('c') # 给节点间添加边 root.add_edge(a) root.add_edge(b) a.add_edge(b) b.add_edge(a) b.add_edge(c) a.add_edge(a) print('ORIGINAL GRAPH:') show_edges(root) # 序列化和反序列化有向图 # 产生一组新的节点 dumped = pickle.dumps(root) reloaded = pickle.loads(dumped) print('\nRELOADED GRAPH:') show_edges(reloaded)
经过序列化和反序列化,这些新的有向图节点对象并不是一开始创建的那些对象,但对象之间的关系保持不变,这可以通过检查对象 id() 返回的值验证。
$ python3 pickle_cycle.py ORIGINAL GRAPH: root -> a (4315798272) a -> b (4315798384) b -> a (4315798272) b -> c (4315799112) a -> a (4315798272) root -> b (4315798384) RELOADED GRAPH: root -> a (4315904096) a -> b (4315904152) b -> a (4315904096) b -> c (4315904208) a -> a (4315904096) root -> b (4315904152