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之画圈还不简单吗?
Sep 20 Python
编写自定义的Django模板加载器的简单示例
Jul 21 Python
python实现微信定时每天和女友发送消息
Apr 29 Python
详解Python 切片语法
Jun 10 Python
numpy.meshgrid()理解(小结)
Aug 01 Python
Python实现Singleton模式的方式详解
Aug 08 Python
python yield关键词案例测试
Oct 15 Python
Python使用多进程运行含有任意个参数的函数
May 02 Python
Django利用elasticsearch(搜索引擎)实现搜索功能
Nov 26 Python
python调用jenkinsAPI构建jenkins,并传递参数的示例
Dec 09 Python
Python 实现劳拉游戏的实例代码(四连环、重力四子棋)
Mar 03 Python
python用海龟绘图写贪吃蛇游戏
Jun 18 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
laravel 5 实现模板主题功能(续)
2015/03/02 PHP
完美解决在ThinkPHP控制器中命名空间的问题
2017/05/05 PHP
QQ邮箱的一个文本编辑器代码
2007/03/14 Javascript
Jquery中显示隐藏的实现代码分析
2011/07/26 Javascript
js 使用form表单select类实现级联菜单效果
2012/12/19 Javascript
移动节点的jquery代码
2014/01/13 Javascript
使用iojs的jsdom库实现同步系统时间
2015/04/20 Javascript
微信小程序引用公共js里的方法的实例详解
2017/08/17 Javascript
JS排序算法之希尔排序与快速排序实现方法
2017/12/12 Javascript
node puppeteer(headless chrome)实现网站登录
2018/05/09 Javascript
ionic grid(栅格)九宫格制作详解
2018/06/30 Javascript
小程序开发踩坑:页面窗口定位(相对于浏览器定位)(推荐)
2019/04/25 Javascript
vue实现codemirror代码编辑器中的SQL代码格式化功能
2019/08/27 Javascript
JS的时间格式化和时间戳转换函数示例详解
2020/07/27 Javascript
[03:09]2014DOTA2国际邀请赛 赛场上的美丽风景线 中国Coser也爱DOTA2
2014/07/20 DOTA
[01:06:25]Secret vs Liquid 2018国际邀请赛淘汰赛BO3 第一场 8.25
2018/08/29 DOTA
从零学python系列之教你如何根据图片生成字符画
2014/05/23 Python
Python实现多线程HTTP下载器示例
2017/02/11 Python
Python数据分析之双色球中蓝红球分析统计示例
2018/02/03 Python
Python自定义线程类简单示例
2018/03/23 Python
Python3.5模块的定义、导入、优化操作图文详解
2019/04/27 Python
python+jinja2实现接口数据批量生成工具
2019/08/28 Python
python输出pdf文档的实例
2020/02/13 Python
python GUI库图形界面开发之PyQt5中QMainWindow, QWidget以及QDialog的区别和选择
2020/02/26 Python
Python的PIL库中getpixel方法的使用
2020/04/09 Python
pyinstaller打包成无控制台程序时运行出错(与popen冲突的解决方法)
2020/04/15 Python
学生如何注册Pycharm专业版以及pycharm的安装
2020/09/24 Python
canvas绘制表情包的示例代码
2018/07/09 HTML / CSS
德国电子产品购物网站:TechInTheBasket德国
2018/12/07 全球购物
英国在线购买马术服装:EQUUS
2019/07/12 全球购物
人力资源部副职的竞聘演讲稿
2014/01/07 职场文书
生产操作工岗位职责
2014/09/16 职场文书
元旦标语大全
2014/10/09 职场文书
银行职员工作失误检讨书
2014/10/14 职场文书
赔偿协议书怎么写
2015/01/28 职场文书
浙江省杭州市平均工资标准是多少?
2019/07/09 职场文书