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实现多线程的两种方式
May 22 Python
利用python生成一个导出数据库的bat脚本文件的方法
Dec 30 Python
Python使用Selenium模块实现模拟浏览器抓取淘宝商品美食信息功能示例
Jul 18 Python
对python3标准库httpclient的使用详解
Dec 18 Python
python pcm音频添加头转成Wav格式文件的方法
Jan 09 Python
django自带serializers序列化返回指定字段的方法
Aug 21 Python
Pytorch Tensor基本数学运算详解
Dec 30 Python
Python绘制动态水球图过程详解
Jun 03 Python
python如何更新包
Jun 11 Python
Python基于gevent实现文件字符串查找器
Aug 11 Python
Python paramiko使用方法代码汇总
Nov 20 Python
python中的插入排序的简单用法
Jan 19 Python
浅谈怎么给Python添加类型标注
Python如何导出导入所有依赖包详解
Jun 08 #Python
OpenCV-Python实现油画效果的实例
OpenCV-Python实现图像平滑处理操作
OpenCV-Python模板匹配人眼的实例
健身房被搭讪?用python写了个小米计时器助人为乐
解决pycharm安装scrapy DLL load failed:找不到指定的程序的问题
You might like
PHP 5.0对象模型深度探索之类的静态成员
2008/03/27 PHP
PHP6 mysql连接方式说明
2009/02/09 PHP
用php守护另一个php进程的例子
2015/02/13 PHP
详解PHP数组赋值方法
2015/11/07 PHP
微信公众平台DEMO(PHP)
2016/05/04 PHP
thinkphp关于简单的权限判定方法
2017/04/03 PHP
PHP提取字符串中的手机号正则表达式怎么写
2017/07/17 PHP
PHP创建自己的Composer包方法
2018/04/09 PHP
getElementsByTagName vs selectNodes效率 及兼容的selectNodes实现
2010/02/26 Javascript
js RuntimeObject() 获取ie里面自定义函数或者属性的集合
2010/11/23 Javascript
jquery插件格式实例分析
2016/06/16 Javascript
详谈JavaScript的闭包及应用
2017/01/17 Javascript
js模拟支付宝密码输入框
2017/04/11 Javascript
利用vue组件自定义v-model实现一个Tab组件方法示例
2017/12/06 Javascript
在react-router4中进行代码拆分的方法(基于webpack)
2018/03/08 Javascript
Nuxt.js实现校验访问浏览器类型的中间件
2018/08/24 Javascript
解决Vue+Electron下Vuex的Dispatch没有效果问题
2019/05/20 Javascript
微信小程序在text文本实现多种字体样式
2019/11/08 Javascript
vue组件实现移动端九宫格转盘抽奖
2020/10/16 Javascript
python编程-将Python程序转化为可执行程序[整理]
2007/04/09 Python
Python批量转换文件编码格式
2015/05/17 Python
Python在图片中添加文字的两种方法
2017/04/29 Python
django 常用orm操作详解
2017/09/13 Python
Python3实现的字典遍历操作详解
2018/04/18 Python
python利用requests库进行接口测试的方法详解
2018/07/06 Python
在pycharm中配置Anaconda以及pip源配置详解
2019/09/09 Python
python matplotlib如何给图中的点加标签
2019/11/14 Python
浅谈Keras参数 input_shape、input_dim和input_length用法
2020/06/29 Python
mysql的最长数据库名,表名,字段名可以是多长
2014/04/21 面试题
SQL Server提供的3种恢复模型都是什么? 有什么区别?
2012/05/13 面试题
临床医学专业毕业生的自我评价
2013/10/17 职场文书
前台接待岗位职责
2013/12/03 职场文书
党员教师一句话承诺
2014/05/30 职场文书
董事长开业致辞
2015/07/29 职场文书
2016年幼儿园教研活动总结
2016/04/05 职场文书
Flask response响应的具体使用
2021/07/15 Python