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控制windows剪贴板,向剪贴板中写入图片的实例
May 31 Python
Django中间件实现拦截器的方法
Jun 01 Python
详解django2中关于时间处理策略
Mar 06 Python
python中的&&及||的实现示例
Aug 07 Python
python实现拉普拉斯特征图降维示例
Nov 25 Python
Jupyter notebook如何实现指定浏览器打开
May 13 Python
python中数字是否为可变类型
Jul 08 Python
python如何建立全零数组
Jul 19 Python
Python3合并两个有序数组代码实例
Aug 11 Python
Django创建一个后台的基本步骤记录
Oct 02 Python
为2021年的第一场雪锦上添花:用matplotlib绘制雪花和雪景
Jan 05 Python
Python自动化之批量处理工作簿和工作表
Jun 03 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目录函数实现创建、读取目录教程实例
2011/01/13 PHP
PHP $_FILES函数详解
2011/03/09 PHP
ThinkPHP3.1.3版本新特性概述
2014/06/19 PHP
ecshop适应在PHP7的修改方法解决报错的实现
2016/11/01 PHP
PHP 二级子目录(后台目录)设置二级域名
2017/03/02 PHP
2017年最新PHP经典面试题目汇总(上篇)
2017/03/17 PHP
PHP以json或xml格式返回请求数据的方法
2018/05/31 PHP
Yii2.0 RESTful API 基础配置教程详解
2018/12/26 PHP
传智播客学习之JavaScript基础篇
2009/11/13 Javascript
UserData用法总结 lanyu出品
2010/07/01 Javascript
用innerhtml提高页面打开速度的方法
2013/08/02 Javascript
javascript中字符串拼接详解
2014/09/26 Javascript
js实现编辑div节点名称的方法
2014/12/17 Javascript
jQuery实现复制到粘贴板功能
2017/02/11 Javascript
大白话讲解JavaScript的Promise
2017/04/06 Javascript
Ionic3 UI组件之autocomplete详解
2017/06/08 Javascript
前端构建工具之gulp的配置与搭建详解
2017/06/12 Javascript
微信小程序--组件(swiper)详细介绍
2017/06/13 Javascript
利用chrome浏览器进行js调试并找出元素绑定的点击事件详解
2021/01/30 Javascript
Javascript查看大图功能代码实现
2020/05/07 Javascript
原生js实现九宫格拖拽换位
2021/01/26 Javascript
Python出现segfault错误解决方法
2016/04/16 Python
使用Template格式化Python字符串的方法
2019/01/22 Python
python图像处理模块Pillow的学习详解
2019/10/09 Python
使用 Python 处理3万多条数据只要几秒钟
2020/01/19 Python
解决Python 函数声明先后顺序出现的问题
2020/09/02 Python
夏威夷灵感服装及配饰:Reyn Spooner
2018/09/18 全球购物
加拿大鞋网:Globo Shoes
2019/12/26 全球购物
美国职棒大联盟的官方手套、球和头盔:Rawlings
2020/02/15 全球购物
介绍一下Make? 为什么使用make
2013/12/08 面试题
私人会所最新创业计划书范文
2014/03/24 职场文书
中学生学雷锋演讲稿
2014/04/26 职场文书
反邪教宣传工作方案
2014/05/07 职场文书
三八节标语
2014/06/27 职场文书
圣诞晚会主持词开场白
2015/05/28 职场文书
Feign调用全局异常处理解决方案
2021/06/24 Java/Android