Python序列化与反序列化相关知识总结


Posted in Python onJune 08, 2021

Python序列化与反序列

在程序运行的过程中,所有的变量都是在内存中,比如,定义一个 dict:

d = dict(name='Bob', age=20, score=88)

可以随时修改变量,比如把 name 改成 ‘Bill',但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的 ‘Bill' 存储到磁盘上,下次重新运行程序,变量又被初始化为 ‘Bob'。

我们把变量从内存中变成可存储或传输的过程称之为序列化,在 Python 中叫 pickling,在其他语言中也被称之为 serialization,marshalling,flattening 等等,都是一个意思。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

Python序列化与反序列化相关知识总结

反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即 unpickling。

Python 提供了 pickle 模块来实现序列化。首先,我们尝试把一个对象序列化并写入文件:

In [1]: import pickle

In [2]: d = dict(name='Bob', age=20, score=88)

In [3]: pickle.dumps(d)
Out[3]: b'\x80\x04\x95$\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x04name\x94\x8c\x03Bob\x94\x8c\x03age\x94K\x14\x8c\x05score\x94KXu.'

pickle.dumps() 方法把任意对象序列化成一个 bytes,然后,就可以把这个 bytes 写入文件。或者用另一个方法 pickle.dump() 直接把对象序列化后写入一个 file-like Object:

In [5]: f = open('dump.txt', 'wb')

In [6]: d = dict(name='Bob', age=20, score=88)

In [7]: pickle.dump(d, f)

In [8]: f.close()

看看写入的 dump.txt 文件,一堆乱七八糟的内容,这些都是 Python 保存的对象内部信息。

Python序列化与反序列化相关知识总结

当我们要把对象从磁盘读到内存时,可以先把内容读到一个 bytes,然后用 pickle.loads() 方法反序列化出对象,也可以直接用 pickle.load() 方法从一个 file-like Object 中直接反序列化出对象。我们打开另一个 Python 命令行来反序列化刚才保存的对象:

In [23]: f = open('dump.txt', 'rb')

In [24]: d = pickle.load(f)

In [25]: f.close()

In [26]: d
Out[26]: {'name': 'Bob', 'age': 20, 'score': 88}

变量的内容又回来了!

当然,这个变量和原来的变量是完全不相干的对象,它们只是内容相同而已。

Pickle 的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于 Python,并且可能不同版本的 Python 彼此都不兼容,因此,只能用 Pickle 保存那些不重要的数据,不能成功地反序列化也没关系。

JSON

如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如 XML,但更好的方法是序列化为 JSON,因为 JSON 表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON 不仅是标准格式,并且比 XML 更快,而且可以直接在 Web 页面中读取,非常方便。

JSON 表示的对象就是标准的 JavaScript 语言的对象,JSON 和 Python 内置的数据类型对应如下:

JSON类型 Python类型
{} dict
[] list
“string” str
1234.56 int 或 float
true/false True/False
null None

Python 内置的 json 模块提供了非常完善的 Python 对象到 JSON 格式的转换。我们先看看如何把 Python对象变成一个 JSON:

In [27]: import json

In [28]: d = dict(name='Bob', age=20, score=88)

In [29]: json.dumps(d)
Out[29]: '{"name": "Bob", "age": 20, "score": 88}'

In [30]: type(json.dumps(d))
Out[30]: str

dumps() 方法返回一个 str,内容就是标准的 JSON。类似的,dump() 方法可以直接把 JSON 写入一个 file-like Object。

要把 JSON 反序列化为 Python 对象,用 loads() 或者对应的 load() 方法,前者把 JSON 的字符串反序列化,后者从 file-like Object 中读取字符串并反序列化:

In [31]: json_str = '{"age": 20, "score": 88, "name": "Bob"}'

In [32]: json.loads(json_str)
Out[32]: {'age': 20, 'score': 88, 'name': 'Bob'}

In [33]: type(json.loads(json_str))
Out[33]: dict

由于 JSON 标准规定 JSON 编码是 UTF-8,所以我们总是能正确地在 Python 的 str 与 JSON 的字符串之间转换。

JSON 进阶

Python 的 dict 对象可以直接序列化为 JSON 的 {},不过,很多时候,我们更喜欢用 class . 表示对象,比如定义 Student 类,然后序列化:

import json

class Student(object):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score

s = Student('Bob', 20, 88)
print(json.dumps(s))

运行代码,毫不留情地得到一个 TypeError:

Traceback (most recent call last):
  ...
TypeError: Object of type Student is not JSON serializable

错误的原因是 Student 对象不是一个可序列化为 JSON 的对象。

如果连 class 的实例对象都无法序列化为 JSON,这肯定不合理!

别急,我们仔细看看 dumps() 方法的参数列表,可以发现,除了第一个必须的 obj 参数外,dumps() 方法还提供了一大堆的可选参数:https://docs.python.org/3/library/json.html#json.dumps

这些可选参数就是让我们来定制 JSON 序列化。前面的代码之所以无法把 Student 类实例序列化为 JSON,是因为默认情况下,dumps() 方法不知道如何将 Student 实例变为一个 JSON 的 {} 对象。

可选参数 default 就是把任意一个对象变成一个可序列为 JSON 的对象,我们只需要为 Student 专门写一个转换函数,再把函数传进去即可:

In [40]: s.name
Out[40]: 'Bob'

In [41]: s.age
Out[41]: 20

In [42]: s.score
Out[42]: 88
def student2dict(std):
    return {
        'name': std.name,
        'age': std.age,
        'score': std.score
    }

这样,Student 实例首先被 student2dict() 函数转换成 dict,然后再被顺利序列化为 JSON:

print(json.dumps(s, default=student2dict))

不过,下次如果遇到一个 Teacher 类的实例,照样无法序列化为 JSON。再写一个函数 也可以,但是我们可以偷个懒,把任意 class 的实例变为 dict:

print(json.dumps(s, default=lambda obj: obj.__dict__))

因为通常 class 的实例都有一个 __dict__ 属性,它就是一个 dict,用来存储实例变量。也有少数例外,比如定义了 __slots__ 的 class。

同样的道理,如果我们要把 JSON 反序列化为一个 Student 对象实例,loads() 方法首先转换出一个 dict 对象,然后,我们传入的 object_hook 函数负责把 dict 转换为 Student 实例:

def dict2student(d):
    return Student(d['name'], d['age'], d['score'])

运行结果如下:

In [48]: json_str = '{"age": 20, "score": 88, "name": "Bob"}'

In [49]: def dict2student(d):
    ...:     return Student(d['name'], d['age'], d['score'])
    ...:

In [50]: print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x1065c6f70>

打印出的是反序列化的 Student 实例对象。

练习

对中文进行 JSON 序列化时,json.dumps() 提供了一个 ensure_ascii 参数,观察该参数对结果的影响:

import json

obj = dict(name='小明', age=20)
s = json.dumps(obj, ensure_ascii=True)
print(s)

小结

Python 语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合 Web 标准,就可以使用 json 模块。

json 模块的 dumps()loads() 函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。

到此这篇关于Python序列化与反序列化相关知识总结的文章就介绍到这了,更多相关Python序列化与反序列内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python查找目录下指定扩展名的文件实例
Apr 01 Python
Python中atexit模块的基本使用示例
Jul 08 Python
Python面向对象编程中关于类和方法的学习笔记
Jun 30 Python
python中while和for的区别总结
Jun 28 Python
Django 源码WSGI剖析过程详解
Aug 05 Python
Python3.0 实现决策树算法的流程
Aug 08 Python
python中类的输出或类的实例输出为这种形式的原因
Aug 12 Python
django-orm F对象的使用 按照两个字段的和,乘积排序实例
May 18 Python
浅谈pymysql查询语句中带有in时传递参数的问题
Jun 05 Python
Python标准库pathlib操作目录和文件
Nov 20 Python
Python学习之时间包使用教程详解
Mar 21 Python
python 闭包函数详细介绍
Apr 19 Python
浅谈怎么给Python添加类型标注
Python如何导出导入所有依赖包详解
Jun 08 #Python
OpenCV-Python实现油画效果的实例
OpenCV-Python实现图像平滑处理操作
OpenCV-Python模板匹配人眼的实例
健身房被搭讪?用python写了个小米计时器助人为乐
解决pycharm安装scrapy DLL load failed:找不到指定的程序的问题
You might like
PHP中4种常用的抓取网络数据方法
2015/06/04 PHP
php获取客户端IP及URL的方法示例
2017/02/03 PHP
javascript 文档的编码问题解决
2009/03/01 Javascript
javascript 闭包疑问
2010/12/30 Javascript
捕获键盘事件(且兼容各浏览器)
2013/07/03 Javascript
解析JSON对象与字符串之间的相互转换
2013/12/18 Javascript
JavaScript检测实例属性, 原型属性
2015/02/04 Javascript
jQuery手机浏览器中拖拽动作的艰难性分析
2015/02/04 Javascript
浅析jQuery Mobile的初始化事件
2015/12/03 Javascript
vue的toast弹窗组件实例详解
2018/05/14 Javascript
微信小程序之批量上传并压缩图片的实例代码
2018/07/05 Javascript
JavaScript实现汉字转换为拼音及缩写的方法示例
2019/03/28 Javascript
基于vue实现web端超大数据量表格的卡顿解决
2019/04/02 Javascript
js时间转换毫秒的实例代码
2019/08/21 Javascript
基于axios 的responseType类型的设置方法
2019/10/29 Javascript
es6 super关键字的理解与应用实例分析
2020/02/15 Javascript
js实现小球在页面规定的区域运动
2020/06/16 Javascript
python 解析html之BeautifulSoup
2009/07/07 Python
详解Python中的文本处理
2015/04/11 Python
两个使用Python脚本操作文件的小示例分享
2015/08/27 Python
深入学习Python中的装饰器使用
2016/06/20 Python
Selenium鼠标与键盘事件常用操作方法示例
2018/08/13 Python
Python 变量类型详解
2018/10/10 Python
python用pandas数据加载、存储与文件格式的实例
2018/12/07 Python
python解析多层json操作示例
2019/12/30 Python
用python按照图像灰度值统计并筛选图片的操作(PIL,shutil,os)
2020/06/04 Python
tensorflow 2.1.0 安装与实战教程(CASIA FACE v5)
2020/06/30 Python
python中openpyxl和xlsxwriter对Excel的操作方法
2021/03/01 Python
HTML5 video标签(播放器)学习笔记(二):播放控制
2015/04/24 HTML / CSS
SIMON MILLER官网:洛杉矶的生活方式品牌
2020/10/19 全球购物
仓库管理制度
2014/01/21 职场文书
《新型玻璃》教学反思
2014/04/13 职场文书
优秀中职教师事迹材料
2014/08/26 职场文书
毕业实习单位意见
2015/06/04 职场文书
幼儿园亲子活动感想
2015/08/07 职场文书
Spring JPA 增加字段执行异常问题及解决
2022/06/10 Java/Android