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实现测试磁盘性能的方法
Mar 12 Python
python中for语句简单遍历数据的方法
May 07 Python
Python argv用法详解
Jan 08 Python
python 简单的多线程链接实现代码
Aug 28 Python
Python实现字典去除重复的方法示例
Jul 31 Python
python 查找文件名包含指定字符串的方法
Jun 05 Python
使用python将图片按标签分入不同文件夹的方法
Dec 08 Python
python实现图像拼接
Mar 05 Python
Sublime Text3最新激活注册码分享适用2020最新版 亲测可用
Nov 12 Python
Python偏函数实现原理及应用
Nov 20 Python
python如何用matplotlib创建三维图表
Jan 26 Python
python Matplotlib基础--如何添加文本和标注
Jan 26 Python
浅谈怎么给Python添加类型标注
Python如何导出导入所有依赖包详解
Jun 08 #Python
OpenCV-Python实现油画效果的实例
OpenCV-Python实现图像平滑处理操作
OpenCV-Python模板匹配人眼的实例
健身房被搭讪?用python写了个小米计时器助人为乐
解决pycharm安装scrapy DLL load failed:找不到指定的程序的问题
You might like
2020显卡排行榜天梯图 显卡天梯图2020年3月最新版
2020/04/02 数码科技
php5数字型字符串加解密代码
2008/04/24 PHP
PHP中使用socket方式GET、POST数据实例
2015/04/02 PHP
php显示时间常用方法小结
2015/06/05 PHP
Javascript this指针
2009/07/30 Javascript
匹配任意字符的正则表达式写法
2010/04/29 Javascript
JS左右无缝滚动(一般方法+面向对象方法)
2012/08/17 Javascript
javascript实现简单的分页特效
2015/08/12 Javascript
使用jQuery+EasyUI实现CheckBoxTree的级联选中特效
2015/12/06 Javascript
JS实现alert中显示换行的方法
2015/12/17 Javascript
微信小程序如何获知用户运行小程序的场景教程
2017/05/17 Javascript
详解vuex 中的 state 在组件中如何监听
2017/05/23 Javascript
jQuery 实现双击编辑表格功能
2017/06/19 jQuery
vue利用axios来完成数据的交互
2018/03/23 Javascript
vue如何通过id从列表页跳转到对应的详情页
2018/05/01 Javascript
JavaScript 2018 中即将迎来的新功能
2018/09/21 Javascript
vue在index.html中引入静态文件不生效问题及解决方法
2019/04/29 Javascript
vue封装可复用组件confirm,并绑定在vue原型上的示例
2019/10/31 Javascript
JavaScript中的this/call/apply/bind的使用及区别
2020/03/06 Javascript
python3实现短网址和数字相互转换的方法
2015/04/28 Python
浅谈Python 中整型对象的存储问题
2016/05/16 Python
教你学会使用Python正则表达式
2017/09/07 Python
用Python3创建httpServer的简单方法
2018/06/04 Python
python执行精确的小数计算方法
2019/01/21 Python
python pandas写入excel文件的方法示例
2019/06/25 Python
对python中不同模块(函数、类、变量)的调用详解
2019/07/16 Python
Python3 venv搭建轻量级虚拟环境的步骤(图文)
2019/08/09 Python
Python的条件锁与事件共享详解
2019/09/12 Python
浅谈html5之sse服务器发送事件EventSource介绍
2017/08/28 HTML / CSS
会计与审计专业自荐信范文
2014/03/15 职场文书
英语教师个人工作总结
2015/02/09 职场文书
幼儿园大班开学寄语(2015秋季)
2015/05/27 职场文书
素质拓展训练感想
2015/08/07 职场文书
mybatis3中@SelectProvider传递参数方式
2021/08/04 Java/Android
Python 数据结构之十大经典排序算法一文通关
2021/10/16 Python
Innodb存储引擎中的后台线程详解
2022/04/03 MySQL