Python3 pickle对象串行化代码实例解析


Posted in Python onMarch 23, 2020

1.pickle对象串行化

pickle模块实现了一个算法可以将任意的Python对象转换为一系列字节。这个过程也被称为串行化对象。可以传输或存储表示对象的字节流,然后再重新构造来创建有相同性质的新对象。

1.1 编码和解码字符串中的数据

第一个例子使用dumps()将一个数据结构编码为一个字符串,然后把这个字符串打印到控制台。它使用了一个完全由内置类型构成的数据结构。任何类的实例都可以pickled,如后面的例子所示。

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

默认的,pickle将以一种二进制格式写入,在Python 3程序之间共享时这种格式兼容性最好。

Python3 pickle对象串行化代码实例解析

数据串行化后,可以写到一个文件、套接字、管道或者其他位置。之后可以读取这个文件,将数据解除pickled,以便用同样的值构造一个新对象。

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对象串行化代码实例解析

1.2 处理流

除了dumps()和loads(),pickle还提供了一些便利函数来处理类似文件的流。可以向一个流写多个对象,然后从流读取这些对象,而无须事先知道要写多少个对象或者这些对象多大。

import io
import pickle
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'))
# Simulate a file.
out_s = io.BytesIO()
# Write to the stream
for o in data:
  print('WRITING : {} ({})'.format(o.name, o.name_backwards))
  pickle.dump(o, out_s)
  out_s.flush()
# Set up a read-able stream
in_s = io.BytesIO(out_s.getvalue())
# Read the data
while True:
  try:
    o = pickle.load(in_s)
  except EOFError:
    break
  else:
    print('READ  : {} ({})'.format(
      o.name, o.name_backwards))

这个例子使用两个BytesIO缓冲区来模拟流。第一个缓冲区接收pickled的对象,它的值被填入第二个缓冲区,load()读取这个缓冲区。简单的数据库格式也可以使用pickle来存储对象。shelve模块就是这样一个实现。

Python3 pickle对象串行化代码实例解析

除了存储数据,pickle对于进程间通信也很方便。例如,os.fork()和os.pipe()可以用来建立工作进程,从一个管道读取作业指令,并把结果写至另一个管道。管理工作线程池以及发送作业和接收响应的核心代码可以重用,因为作业和响应对象不必基于一个特定的类。使用管道或套接字时,在转储各个对象之后不要忘记刷新输出,以便将数据通过连接推送到另一端。参见multiprocessing模块来了解一个可重用的工作线程池管理器。

1.3 重构对象的问题

处理定制类时,pickled的类必须出现在读取pickle的进程所在的命名空间里。只会pickled这个实例的数据,而不是类定义。类名用于查找构造函数,以便在解除pickled时参见新对象。下面这个例子将一个类的实例写至一个文件。

import pickleclass 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'))
  with open('Test.py', 'wb') as out_s:
    for o in data:
      print('WRITING: {} ({})'.format(
        o.name, o.name_backwards))
      pickle.dump(o, out_s)

运行这个脚本时,会根据作为命令行参数给定的名字来创建一个文件。

Python3 pickle对象串行化代码实例解析

通过简单的尝试加载而得到的pickled对象将会失败。

import pickle
with open('Test.py', '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对象串行化代码实例解析

修正后的版本从原脚本导入了SimpleObject,这一次运行会成功。在导入列表的最后增加了import语句后,现在脚本就能找到这个类并构造对象了。

from demo import SimpleObject

现在允许修改后的脚本会生成期望的结果。

Python3 pickle对象串行化代码实例解析

1.4Unpicklable的对象

并不是所有对象都是可pickled的。套接字、文件句柄、数据库连接以及其他运行时状态依赖于操作系统或其他进程的对象,其可能无法用一种有意义的方式保存。如果对象包含不可pickled的属性,则可以定义__getstate__()和__setstate__()来返回所pickled实例的状态的一个子集。

__getstate__()方法必须返回一个对象,其中包含所pickled对象的内部状态。表示状态的一种便利方式是使用字典,不过值可以是任意的可pickled对象。保存状态,然后再从pickle加载对象时将所保存的状态传入__setstate__()。

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)

这个例子使用了一个单独的State对象来保存MyClass的内部状态。从pickle加载MyClass的一个实例时,会向__setstate__()传入一个State实例,用来初始化这个对象。

Python3 pickle对象串行化代码实例解析

1.5 循环引用

pickle协议会自动处理对象之间的循环引用,所以复杂数据结构不需要任何特殊的处理。

import pickle
class Node:
  """A simple digraph
  """
  def __init__(self, name):
    self.name = name
    self.connections = []
  def add_edge(self, node):
    "Create an edge between this node and the other."
    self.connections.append(node)
  def __iter__(self):
    return iter(self.connections)
def preorder_traversal(root, seen=None, parent=None):
  """Generator function to yield the edges in a graph.
  """
  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):
  "Print all the edges in the graph."
  for parent, child in preorder_traversal(root):
    if not parent:
      continue
    print('{:>5} -> {:>2} ({})'.format(
      parent.name, child.name, id(child)))
# Set up the nodes.
root = Node('root')
a = Node('a')
b = Node('b')
c = Node('c')
# Add edges between them.
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)
# Pickle and unpickle the graph to create
# a new set of nodes.
dumped = pickle.dumps(root)
reloaded = pickle.loads(dumped)
print('\nRELOADED GRAPH:')
show_edges(reloaded)

重新加载的节点并不是同一个对象,但保持了节点之间的关系,而且如果对象有多个引用,那么只会重新加载这个对象的一个副本。要验证这两点,可以在通过pickle传递节点之前和之后检查节点的id()值。

Python3 pickle对象串行化代码实例解析

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
爬山算法简介和Python实现实例
Apr 26 Python
在Python中操作时间之tzset()方法的使用教程
May 22 Python
python在TXT文件中按照某一字符串取出该字符串所在的行方法
Dec 10 Python
python scatter散点图用循环分类法加图例
Mar 19 Python
Python中使用pypdf2合并、分割、加密pdf文件的代码详解
May 21 Python
tensorflow如何批量读取图片
Aug 29 Python
django中url映射规则和服务端响应顺序的实现
Apr 02 Python
python如何支持并发方法详解
Jul 25 Python
安装不同版本的tensorflow与models方法实现
Feb 20 Python
使用Python脚本对GiteePages进行一键部署的使用说明
May 27 Python
Django框架之路由用法
Jun 10 Python
python中validators库的使用方法详解
Sep 23 Python
Python面向对象程序设计之类和对象、实例变量、类变量用法分析
Mar 23 #Python
Python3 shelve对象持久存储原理详解
Mar 23 #Python
python新式类和经典类的区别实例分析
Mar 23 #Python
Python count函数使用方法实例解析
Mar 23 #Python
使用python实现飞机大战游戏
Mar 23 #Python
如何在Django中使用聚合的实现示例
Mar 23 #Python
python3注册全局热键的实现
Mar 22 #Python
You might like
使用php4加速网络传输
2006/10/09 PHP
php checkbox复选框值的获取与checkbox默认值输出方法
2010/05/15 PHP
php之Smarty模板使用方法示例详解
2014/07/08 PHP
PHP排序算法之快速排序(Quick Sort)及其优化算法详解
2018/04/21 PHP
宝塔面板在NGINX环境中TP5.1如何运行?
2021/03/09 PHP
js操作select控件的几种方法
2010/06/02 Javascript
理解JS事件循环
2016/01/07 Javascript
JavaScript事件学习小结(一)事件流
2016/06/09 Javascript
AngularJS ng-bind 指令简单实现
2016/07/30 Javascript
手机端点击图片放大特效PhotoSwipe.js插件实现
2016/08/24 Javascript
JS闭包用法实例分析
2017/03/27 Javascript
微信小程序 商城开发(ecshop )简单实例
2017/04/07 Javascript
使用pkg打包Node.js应用的方法步骤
2018/10/19 Javascript
Electron 如何调用本地模块的方法
2019/02/01 Javascript
javascript 函数的暂停和恢复实例详解
2020/04/25 Javascript
python判断端口是否打开的实现代码
2013/02/10 Python
零基础写python爬虫之爬虫编写全记录
2014/11/06 Python
python查询mysql中文乱码问题
2014/11/09 Python
Python实现批量将word转html并将html内容发布至网站的方法
2015/07/14 Python
利用Python爬虫给孩子起个好名字
2017/02/14 Python
详解python上传文件和字符到PHP服务器
2017/11/24 Python
解决使用pycharm提交代码时冲突之后文件丢失找回的方法
2018/08/05 Python
VPS CENTOS 上配置python,mysql,nginx,uwsgi,django的方法详解
2019/07/01 Python
CSS3截取字符串实例代码【推荐】
2018/06/07 HTML / CSS
迪奥官网:Dior.com
2018/12/04 全球购物
教育课题研究自我鉴定范文
2013/12/28 职场文书
行政专员岗位职责
2014/01/02 职场文书
快递员岗位职责
2014/09/12 职场文书
带刀到教室的检讨书
2014/10/04 职场文书
2015年房产经纪人工作总结
2015/05/15 职场文书
2016计算机专业毕业生自荐信
2016/01/28 职场文书
学校团代会开幕词
2016/03/04 职场文书
2019七夕节祝福语36句,快来收藏吧
2019/08/06 职场文书
Python Pandas模块实现数据的统计分析的方法
2021/06/24 Python
5种 JavaScript 方式实现数组扁平化
2021/10/05 Javascript
Win10 heic文件怎么打开 ? Win10 heic文件打开教程
2022/04/06 数码科技