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实现检测服务器是否可以ping通的2种方法
Jan 01 Python
Python面向对象程序设计类变量与成员变量、类方法与成员方法用法分析
Apr 12 Python
django admin组件使用方法详解
Jul 19 Python
python中for循环变量作用域及用法详解
Nov 05 Python
win10系统下python3安装及pip换源和使用教程
Jan 06 Python
Python函数式编程实例详解
Jan 17 Python
Python reversed函数及使用方法解析
Mar 17 Python
使用Pycharm分段执行代码
Apr 15 Python
哪种Python框架适合你?简单介绍几种主流Python框架
Aug 04 Python
Pycharm常用快捷键总结及配置方法
Nov 14 Python
python 生成正态分布数据,并绘图和解析
Dec 21 Python
python中Pyqt5使用Qlabel标签播放视频
Apr 22 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
php过滤HTML标签、属性等正则表达式汇总
2014/09/22 PHP
php使用ob_start()实现图片存入变量的方法
2014/11/14 PHP
PHP如何通过传引用的思想实现无限分类(代码简单)
2015/10/13 PHP
PHP将身份证正反面两张照片合成一张图片的代码
2017/04/08 PHP
PHP长网址与短网址的实现方法
2017/10/13 PHP
PHP耦合设计模式实例分析
2018/08/08 PHP
PHP addslashes()函数讲解
2019/02/03 PHP
Yii2框架加载css和js文件的方法分析
2019/05/25 PHP
javascript之水平横向滚动歌词同步的应用
2007/05/07 Javascript
javascript 跨浏览器开发经验总结(五) js 事件
2010/05/19 Javascript
关于Javascript与iframe的那些事儿
2013/07/04 Javascript
原生JS封装ajax 传json,str,excel文件上传提交表单(推荐)
2016/06/21 Javascript
jQuery插件HighCharts绘制2D圆环图效果示例【附demo源码下载】
2017/03/09 Javascript
基于jQuery实现文字打印动态效果
2017/04/21 jQuery
bootstrap+jQuery实现的动态进度条功能示例
2017/05/25 jQuery
JS解决position:sticky的兼容性问题的方法
2017/10/17 Javascript
关于ES6箭头函数中的this问题
2018/02/27 Javascript
安装vue-cli的简易过程
2018/05/22 Javascript
JS实现无限轮播无倒退效果
2020/09/21 Javascript
jquery实现点击左右按钮切换图片
2021/01/27 jQuery
详解Python 实现元胞自动机中的生命游戏(Game of life)
2018/01/27 Python
python深度优先搜索和广度优先搜索
2018/02/07 Python
Django 实现外键去除自动添加的后缀‘_id’
2019/11/15 Python
Flask和pyecharts实现动态数据可视化
2020/02/26 Python
Python的控制结构之For、While、If循环问题
2020/06/30 Python
Python钉钉报警及Zabbix集成钉钉报警的示例代码
2020/08/17 Python
会计毕业生自我鉴定
2013/11/04 职场文书
入党自我评价范文
2014/02/02 职场文书
企业军训感想
2014/02/07 职场文书
餐饮总经理岗位职责
2014/03/07 职场文书
师恩难忘教学反思
2014/04/27 职场文书
三分钟自我介绍演讲稿
2014/08/21 职场文书
2014小学一年级班主任工作总结
2014/12/05 职场文书
研究生简历自我评
2015/03/11 职场文书
2015年幼儿园保育工作总结
2015/05/12 职场文书
浅谈node.js中间件有哪些类型
2021/04/29 Javascript