1. pickle 简介

2. pickle 核心函数

3. pickle 高级 —— 复杂对象

1. 持久化与 pickle 简介

1.1 什么是持久化?

持久化的基本思想很简单。假定有一个 Python 程序,它可能是一个管理日常待办事项的程序,你希望在多次执行这个程序之间可以保存应用程序对象(待办事项)。换句话说,你希望将对象存储在磁盘上,便于以后检索,这就是持久化。要达到这个目的,有几种方法,每一种方法都有其优缺点。

例如,可以将对象数据存储在某种格式的文本文件中,譬如 CSV 文件。或者可以用关系数据库,譬如 Gadfly、MySQL、PostgreSQL 或者 DB2。这些文件格式和数据库都非常优秀,对于所有这些存储机制,Python 都有健壮的接口。

这些存储机制都有一个共同点:存储的数据是独立于对这些数据进行操作的对象和程序。这样做的好处是,数据可以作为共享的资源,供其它应用程序使用。缺点是,用这种方式,可以允许其它程序访问对象的数据,这违背了面向对象的封装性原则 — 即对象的数据只能通过这个对象自身的公共(public)接口来访问

另外,对于某些应用程序,关系数据库方法可能不是很理想。尤其是,关系数据库不理解对象。相反,关系数据库会强行使用自己的类型系统和关系数据模型(表),每张表包含一组元组(行),每行包含具有固定数目的静态类型字段(列)。如果应用程序的对象模型不能够方便地转换到关系模型,那么在将对象映射到元组以及将元组映射回对象方面,会碰到一定难度。这种困难常被称为阻碍性不匹配(impedence-mismatch)问题。

1.2 对象持久性

如果希望透明地存储 Python 对象,而不丢失其身份和类型等信息,则需要某种形式的对象序列化:它是一个将任意复杂的对象转成对象的文本或二进制表示的过程。同样,必须能够将对象经过序列化后的形式恢复到原有的对象。在 Python 中,这种序列化过程称为 pickle,可以将对象 pickle 成字符串、磁盘上的文件或者任何类似于文件的对象,也可以将这些字符串、文件或任何类似于文件的对象 unpickle 成原来的对象。

假定您喜欢将任何事物都保存成对象,而且希望避免将对象转换成某种基于非对象存储的开销;那么 pickle 文件可以提供这些好处,但有时可能需要比这种简单的 pickle 文件更健壮以及更具有可伸缩性的事物。例如,只用 pickle 不能解决命名和查找 pickle 文件这样的问题,另外,它也不能支持并发地访问持久性对象。如果需要这些方面的功能,则要求助类似于 ZODB(针对 Python 的 Z 对象数据库)这类数据库。ZODB 是一个健壮的、多用户的和面向对象的数据库系统,它能够存储和管理任意复杂的 Python 对象,并支持事务操作和并发控制。令人足够感兴趣的是,甚至 ZODB 也依靠 Python 的本机序列化能力。而要想有效地使用 ZODB,首先必须充分了解 pickle。

1.3 pickle 模块简介

pickle 提供了一个简单的持久化功能。可以将对象以文件的形式存放在磁盘上。

Python 中几乎所有的数据类型(列表、字典、集合、类等)都可以用 pickle 来序列化。pickle 序列化后的数据,可读性差,人一般无法识别。

pickle 的可移植性

从空间和时间上说,Pickle 是可移植的。例如,可以在 Linux 下创建一个 pickle,然后将它发送到在 Windows 或 Mac OS 下运行的 Python 程序。并且,当升级到更新版本的 Python 时,不必担心可能要废弃已有的 pickle。Python 开发人员已经保证 pickle 格式将可以向后兼容 Python 各个版本。事实上,在 pickle 模块中提供了有关目前以及所支持的格式方面的详细信息。

2. pickle 函数

pickle 模块提供了以下函数:

  1. dumps(object):将 python 对象转换(序列化)为字节(二进制)对象。
  2. loads(string):将二进制对象转换(反序列为)为 python 对象。
  3. dump(object, file):将对象写到文件,这个文件可以是实际的物理文件,也可以是任何类似于文件的对象,这个对象具有 write() 方法,可以接受单个的字符串参数。
  4. load(file):返回包含在 pickle 文件中的对象。

缺省情况下, dumps() 和 dump() 使用可打印的 ASCII 表示来创建 pickle。两者都有一个 final 参数(可选,如果为 True,则该参数指定用更快以及更小的二进制表示来创建 pickle)。loads() 和 load() 函数则会自动检测 pickle 是二进制格式还是文本格式。事实上,在 pickle 模块中记录了所有使用的约定。

2.1 dumps() 和 loads()

  • dumps(object):将 python 对象转换(序列化)为字节(二进制)对象。
  • loads(string):将二进制对象转换(反序列为)为 python 对象。

示例:

# 将对象序列化为二进制
>>> o1 = ("this is string", 42, [1,2,3], {1:2}, None)
>>> p1 = pickle.dumps(o1)
>>> print(p1)
b'\x80\x03(X\x0e\x00\x00\x00this is stringq\x00K*]q\x01(K\x01K\x02K\x03e}q\x02K\
x01K\x02sNtq\x03.' # 将二进制反序列化为对象
>>> o2 = pickle.loads(p1)
>>> print(o2)
('this is string', 42, [1, 2, 3], {1: 2}, None)

在以上示例中,使用的都是简单对象,因此使用二进制 pickle 格式不会在节省空间上显示出太大的效率。然而,在实际使用复杂对象的系统中,使用二进制格式可以在大小和速度方面带来显著的改进。

2.2 dump() 和 load()

  • dump(object, file):将 python 对象写(序列化)到文件,这个文件可以是实际的物理文件,也可以是任何类似于文件的对象,这个对象具有 write() 方法,可以接受单个的字符串参数。
  • load(file):从文件中将二进制对象读取(反序列化)为 python 对象。

dump() 和 load() ,它们使用文件和类似文件的对象。这些函数的操作非常类似于我们刚才所看到的 dumps() 和 loads() ,区别在于它们还有另一种能力 — dump() 函数能一个接着一个地将几个对象转储到同一个文件。随后调用 load() 来以同样的顺序检索这些对象

示例:

 1 # 将对象序列化到二进制文件中
2 >>> a1 = "apple"
3 >>> b1 = {1:"one", 2:"two"}
4 >>> c1 = [1,"two"]
5 >>> f1 = open("tmp.pkl", "wb")
6 >>> pickle.dump(a1, f1)
7 >>> pickle.dump(b1, f1)
8 >>> pickle.dump(c1, f1)
9 >>> f1.close()
10
11 # 按序(先入先出)从二进制文件中反序列为对象
12 >>> f2 = open("tmp.pkl", "rb")
13 >>> a2 = pickle.load(f2)
14 >>> a2
15 'apple'
16 >>> b2 = pickle.load(f2)
17 >>> b2
18 {1: 'one', 2: 'two'}
19 >>> c2 = pickle.load(f2)
20 >>> c2
21 [1, 'two']
22 >>> f2.close()

3. pickle 高级 —— 复杂对象

到目前为止,我们讲述了关于 pickle 方面的基本知识。在这一节,将讨论一些高级问题,当你开始 pickle 复杂对象时,会遇到这些问题,其中包括定制类的实例。幸运的是,Python 可以很容易地处理这种情形。

3.1 多个引用,同一对象

在 Python 中,变量是对象的引用。同时,也可以用多个变量引用同一个对象。经证明,Python 在用经过 pickle 的对象维护这种行为方面丝毫没有困难。

示例:对象引用的维护

 1 >>> a = [1,2,3]
2 >>> b = a
3 >>> a
4 [1, 2, 3]
5 >>> b
6 [1, 2, 3]
7 >>>
8 >>> c = pickle.dumps((a,b))
9 >>> d, e = pickle.loads(c)
10 >>> d
11 [1, 2, 3]
12 >>> e
13 [1, 2, 3]
14 >>> d.append(4)
15 >>> e
16 [1, 2, 3, 4]

3.2 循环引用和递归引用

可以将刚才演示过的对象引用支持扩展到 递归引用(一个对象包含对其自身的引用)和 循环引用(两个对象各自包含对对方的引用)。

示例:递归引用

 1 >>> li = [1,2,3]
2 >>> li.append(li)
3 >>> li
4 [1, 2, 3, [...]]
5 >>> li[3]
6 [1, 2, 3, [...]]
7 >>> li[3][3]
8 [1, 2, 3, [...]]
9 >>> p = pickle.dumps(li)
10 >>> li2 = pickle.loads(p)
11 >>> li2
12 [1, 2, 3, [...]]
13 >>> li2[3]
14 [1, 2, 3, [...]]
15 >>> li2[3][3]
16 [1, 2, 3, [...]]

示例:循环引用

 1 >>> a = [1,2]
2 >>> b = [3,4]
3 >>> a.append(b)
4 >>> b.append(a)
5 >>> a
6 [1, 2, [3, 4, [...]]]
7 >>> b
8 [3, 4, [1, 2, [...]]]
9 >>> a[2]
10 [3, 4, [1, 2, [...]]]
11 >>> a[2] is b
12 True
13 >>> f1 = open("tmp.pkl","wb")
14 >>> pickle.dump((a,b), f1)
15 >>> f1.close()
16 >>>
17 >>> f2 = open("tmp.pkl", "rb")
18 >>> c,d = pickle.load(f2)
19 >>> f2.close()
20 >>> c
21 [1, 2, [3, 4, [...]]]
22 >>> d
23 [3, 4, [1, 2, [...]]]
24 >>> c[2] is d
25 True

3.3 分别 pickle vs. 在一个元组中一起 pickle

如果分别 pickle 每个对象,而不是在一个元组中一起 pickle 所有对象,会得到略微不同(但很重要)的结果:在 pickle 情形中,每个对象被恢复到一个与原来对象相等的对象,但不是同一个对象。换句话说,每个 pickle 都是原来对象的一个副本。

 1 # 通过元组一起pickle
2 >>> a = [1,2]
3 >>> b = a
4 >>> f1 = open("tmp.pkl", "wb")
5 >>> pickle.dump((a,b), f1)
6 >>> f1.close()
7 >>>
8 >>> f2 = open("tmp.pkl", "rb")
9 >>> c,d = pickle.load(f2)
10 >>> c is a
11 False
12 >>> c is d
13 True
14 >>>
15
16 # 分别pickle
17 >>> f3 = open("tmp.pkl", "wb")
18 >>> f3.close()
19 >>> a2 = [1,2]
20 >>> b2 = a2
21 >>> f3 = open("tmp.pkl", "wb")
22 >>> pickle.dump(a2, f3)
23 >>> pickle.dump(b2, f3)
24 >>> f3.close()
25 >>>
26 >>> f4 = open("tmp.pkl", "rb")
27 >>> c2 = pickle.load(f4)
28 >>> d2 = pickle.load(f4)
29 >>> f4.close()
30 >>>
31 >>> c2
32 [1, 2]
33 >>> d2
34 [1, 2]
35 >>> c2 is d2
36 False

3.4 维护分别 pickle 的对象间的引用

有一个选项确实允许分别 pickle 对象,并维护相互之间的引用,只要这些对象都是 pickle 到同一文件即可。 pickle 模块提供了一个 Pickler (与此相对应是 Unpickler ),它能够跟踪已经被 pickle 的对象。通过使用这个 Pickler ,将会通过引用而不是通过值来 pickle 共享和循环引用。

 1 >>> f1 = open("tmp.pkl", "wb")
2 >>> pickler = pickle.Pickler(f1)
3 >>> a = [1,2]
4 >>> b = a
5 >>> pickler.dump(a)
6 >>> pickler.dump(b)
7 >>> f1.close()
8 >>>
9 >>> f2 = open("tmp.pkl", "rb")
10 >>> unpickler = pickle.Unpickler(f2)
11 >>> c = unpickler.load()
12 >>> d = unpickler.load()
13 >>> c
14 [1, 2]
15 >>> d
16 [1, 2]
17 >>> c is d # 注意与上一个示例的结果不同
18 True

3.5 不可 pickle 的对象

一些对象类型是不可 pickle 的。例如,Python 不能 pickle 文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不能保证它可以重建该文件的状态。试图 pickle 文件对象会导致以下错误:

1 >>> f = open("tmp.pkl", "wb")
2 >>> pickle.dumps(f)
3 Traceback (most recent call last):
4 File "<stdin>", line 1, in <module>
5 TypeError: cannot serialize '_io.BufferedWriter' object

3.6 类实例

与 pickle 简单对象类型相比,pickle 类实例要多加留意。这主要由于 Python 会 pickle 实例数据(通常是 _dict_ 属性)和类的名称,而不会 pickle 类的代码。当 Python unpickle 类的实例时,它会试图使用在 pickle 该实例时的确切的类名称和模块名称(包括任何包的路径前缀)导入包含该类定义的模块。另外要注意,类定义必须出现在模块的最顶层,这意味着它们不能是嵌套的类(在其它类或函数中定义的类)。

当 unpickle 类的实例时,通常不会再调用它们的 _init_() 方法。相反,Python 创建一个通用类实例,并应用已进行过 pickle 的实例属性,同时设置该实例的 _class_ 属性,使其指向原来的类。

对 Python 2.2 中引入的新型类进行 unpickle 的机制与原来的略有不同。虽然处理的结果实际上与对旧型类处理的结果相同,但 Python 使用 copy_reg 模块的 _reconstructor() 函数来恢复新型类的实例。

如果希望对新型或旧型类的实例修改缺省的 pickle 行为,则可以定义特殊的类的方法 _getstate_() 和 _setstate_() ,在保存和恢复类实例的状态信息期间,Python 会调用这些方法。

序列化 pickle模块的更多相关文章

  1. Python第十四天 序列化 pickle模块 cPickle模块 JSON模块 API的两种格式

    Python第十四天 序列化  pickle模块  cPickle模块  JSON模块  API的两种格式 目录 Pycharm使用技巧(转载) Python第一天  安装  shell  文件 Py ...

  2. 序列化pickle模块

    1.pickle模块 pickle.dumps() 和pickle.loads() import pickle f = open('112.pkl','w') a = {'name':2,2:3,3: ...

  3. Python--模块之sys模块、logging模块、序列化json模块、序列化pickle模块

    sys模块 sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit() sys.path 返回模块的搜索路径,初始化时使用PYTHONPA ...

  4. os模块、sys模块、json模块、pickle模块、logging模块

    目录 os模块 sys模块 json模块 pickle模块 logging模块 os模块 功能:与操作系统交互,可以操作文件 一.对文件操作 判断是否为文件 os.path.isfile(r'路径') ...

  5. 各类模块的粗略总结(time,re,os,sys,序列化,pickle,shelve.#!json )

    ***collections 扩展数据类型*** ***re 正则相关操作 正则 匹配字符串*** ***time 时间相关 三种格式:时间戳,格式化时间(字符串),时间元组(结构化时间).***`` ...

  6. Pythoy 数据类型序列化——json&pickle 模块

    Pythoy 数据类型序列化--json&pickle 模块 TOC 什么是序列化/反序列化 pickle 模块 json 模块 对比json和pickle json.tool 命令行接口 什 ...

  7. [python](windows)分布式进程问题:pickle模块不能序列化lambda函数

    运行错误:_pickle.PicklingError: Can't pickle <function <lambda> at 0x000002BAAEF12F28>: attr ...

  8. 序列化模块— json模块,pickle模块,shelve模块

    json模块 pickle模块 shelve模块 序列化——将原本的字典.列表等内容转换成一个字符串的过程就叫做序列化. # 序列化模块 # 数据类型转化成字符串的过程就是序列化 # 为了方便存储和网 ...

  9. python 常用模块(一): os模块,序列化模块(json模块 pickle模块 )

    1.os模块 2.序列化模块:(1)json模块 和 pickle模块 一.os模块 os.path.abspath: (1)把路径中不符合规范的/改成操作系统默认的格式 import os path ...

随机推荐

  1. 清晰图解深度分析HTTPS原理

    前言 很高兴遇见你~ Https现在基本已经覆盖所有的http请求了,作为一个伟大的发明,保障了我们的通信安全.在Android中对于HTTPS其实感知不多,因为这些内容都有成熟的框架帮我们完成了,例 ...

  2. SOLID架构设计原则

    最近通读了<架构整洁之道>,受益匪浅,遂摘选出设计原则部分,与大家分享,希望大家能从中获益. 以下为书中第3部分 设计原则的原文. 设计原则概述 通常来说,要想构建-个好的软件系统,应该从 ...

  3. POJ-1321棋盘问题(简单深搜)

    简单搜索step1 POJ-1321 这是第一次博客,题目也很简单,主要是注意格式书写以及常见的快速输入输出和文件输入输出的格式. 递归的时候注意起始是从(-1,-1)开始,然后每次从下一行开始递归. ...

  4. HDOJ-1540(线段树+较复杂的单点修改和区间查询)

    Tunnel Warfare HDOJ-1540 这题关于线段树的操作有一定的难度,需要较好的思维能力. 关于题目的详细解答已经在代码中体现了. #include<iostream> #i ...

  5. centos命令上传

    首先安装 lrzsz # yum -y install lrzsz 运行 rz 命令: 在弹出的窗口选择需要上传的文件,文件会被上传至对应的目录下 运行 sz file.name 在弹出的窗口选择保存 ...

  6. [unknown source] 快乐树

    一.题目 题目描述 有一棵 \(n\) 个节点的数,每个点有点权 \(a_i\),定义一条路径的权值为路径上所有点的异或和,求所有路径的权值和,有 \(q\) 次修改,每次改一个点的点权. 数据范围 ...

  7. android分析之Parcel

    将数据打包,跨进程传输(通过Binder).看看这货究竟是啥玩意: Parcel.java : public final class Parcel { private static final boo ...

  8. ASP.NET Core扩展库之实体映射

    在分层设计模式中,各层之间的数据通常通过数据传输对象(DTO)来进行数据的传递,而大多数情况下,各层数据的定义结构大同小异,如何在这些定义结构中相互转换,之前我们通过使用AutoMapper库,但Au ...

  9. Linux 三剑客之 awk 实战详解教程

    我们知道 Linux 三剑客,它们分别是:grep.sed.awk.在前边已经讲过 grep 和 sed,没看过的同学可以直接点击阅读,今天要分享的是更为强大的 awk. sed 可以实现非交互式的字 ...

  10. Linux wget 使用笔记

    wget 是 Linux 上最常用的文件下载工具,简单实用.记录一下一些常用的操作备查. 最常用最简单的操作就是直接使用一个 URL 下载 下载互联网上指定的文件 wget https://gemme ...