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. Example之selectOneByExample方法和selectByExample的使用

    selectOneByExample示例如下: Example userExample = new Example(User.class);userExample.createCriteria().a ...

  2. 搭载华为麒麟9000的Mate X2:秒售罄,一机难求

    本文首发 | 公众号:lunvey 昨日10点,搭载了麒麟9000芯片的华为Mate X2正式开售,定价17999,对于手机来说,价格实在是高昂. 虽然价格高昂,但是一分钱一分货,对于想抢先体验的网友 ...

  3. Blackduck的Hub安装教程

    1 产品介绍 Black Duck 是最早进行开源代码检测工具开发的公司,其产品包括Protex 和HUB,Protex 强调检测的精度和准确性,而HUB 强调检测的速度和易用性. 1.1 Prote ...

  4. go mod包管理 加代理下载

    原始go.mod文件 module xxx go 1.14 报错 i/o timeout go mod init workorder go mod init: go.mod already exist ...

  5. iot漏洞mips汇编基础

    1 基础概念 MIPS(Microprocessor without Interlocked Piped Stages architecture),是一种采取精简指令集(RISC)的处理架构,由MIP ...

  6. 在Asp.Net Core 5 中使用EF Core连接MariaDB

    升级到Asp.Net Core 5,使用EF Core连接MariaDB,使用的Nuget包Pomelo.EntityFrameworkCore.MySql也升级到了5.0.0-alpha.2,然后发 ...

  7. Tomcat8弱口令+后台getshell

    漏洞原因 用户权限在conf/tomcat-users.xml文件中配置: <?xml version="1.0" encoding="UTF-8"?&g ...

  8. Redis工具收费后新的开源已出现

    作者:三十三重天 博客: zhouhuibo.club 引言 Redis工具哪家强,中国山东找蓝翔.哎呀,串台了. 众所周知,开源的最终还是收费. Reids Desktop 秉承了这一理念,苦逼的程 ...

  9. 【关系抽取-R-BERT】模型结构

    模型的整体结构 相关代码 import torch import torch.nn as nn from transformers import BertModel, BertPreTrainedMo ...

  10. C++11多线程编程(常见面试题)

    [题目1] 子线程循环 10 次,接着主线程循环 100 次,接着又回到子线程循环 10 次,接着再回到主线程又循环 100 次,如此循环50次,试写出代码 [题解] 首先我们来分析一下这道题...( ...