引言

在日常开发中,所有的对象都是存储在内存当中,尤其是像 python 这样的坚持一切接对象的高级程序设计语言,一旦关机,在写在内存中的数据都将不复存在。另一方面,存储在内存够中的对象由于编程语言、网络环境等等因素,很难在网络中进行传输交互。由此,就诞生了一种机制,可以实现内存中的对象与方便持久化在磁盘中或在网络中进行交互的数据格式(str、bites)之间的相互转换。这种机制就叫序列化与反序列化:

序列化:将内存中的不可持久化和传输对象转换为可方便持久化和传输对象的过程。

反序列化:将可持久化和传输对象转换为不可持久化和传输对象的过程。

Python 中提供 pickle 和 json 两个模块来实现序列化与反序列化,pickle 模块和 json 模块 dumps()、dump()、loads()、load()这是个函数,其中 dumps()、dump()用于实现序列化,loads()、load()用于实现反序列化。下面,我们分别对 pickle 和 json 模块进行介绍。

pickle 模块

​ pickle 模块的 dumps()、dump()、loads()、load() 是个函数按功能划分可以分为两组:

  • 序列化:dumps()、dump()
  • 反序列化:loads()、load()

dumps()与 dump()的区别是 dumps()只是单纯得将对象序列化,而 dump()会在序列化之后将结果写入到文件当中;与之对应,loads()与 load()区别至于 loads()是对 dumps 的序列化结果进行反序列化,而 load()会从文件中读取内容进行反序列化。

dumps()与 loads()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> import pickle
>>> p_dict = {'name':' 张三 ' , 'age':30 , 'isMarried':False} # 定义一个字典
>>> p_str = pickle.dumps(p_dict) # 序列化
>>> type(p_dict)
<class 'dict'>
>>> type(p_str)
<class 'bytes'>
>>> p_str
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x06\x00\x00\x00\xe5\xbc\xa0\xe4\xb8\x89q\x02X\x03\x00\x00\x00ageq\x03K\x1eX\t\x00\x00\x00isMarriedq\x04\x89u.'
>>> p = pickle.loads(p_str)
>>> type(p)
<class 'dict'>
>>> p
{'name': ' 张三 ', 'age': 30, 'isMarried': False}

可以看到,反序列化后得到的 p 和序列化之前的 p_dict 内容是一模一样的。不过,p 与 p_dict 已经是两个不同的对象了:

1
2
>>> id(p)==id(p_dict)
False

dump()与 load()

序列化:

1
2
3
4
5
>>> import pickle
>>> p_dict = {'name':' 张三 ' , 'age':30 , 'isMarried':False} # 定义一个字典
>>> file = open("my_dump.txt", "wb") # 因为序列化只有的是 bites 类型,所以必须以 wb 模式打开
>>> pickle.dump(p_dict, file)
>>> file.close()

此时,通过上面代码我们已经将 p_dict 序列化成功,并写入到了一个名为 my_dump.txt 文件中。你可以找到这个文件,然后将它拷贝到任何电脑上进行反序列化:

1
2
3
4
5
6
7
>>> file=open("my_dump.txt","rb")
>>> p=pickle.load(file)
>>> file.close()
>>> type(p)
<class 'dict'>
>>> p
{'name': ' 张三 ', 'age': 30, 'isMarried': False}

反序列化后得到的内容与序列化之前的内容完全一样。体会到序列化与反序列化的作用了吗?序列化之后的内容可以方便得保存到磁盘中,电脑关机也不怕。

json 模块

如果你阅读并理解了上文中关于 pickle 的部门内容,对于这一部分的 json 模块内容,你可以不费吹灰之力掌握。上文中说到过,与 pickle 一样,json 模块也提供了 dumps()、dump()、loads()、load()则是个函数,且其中区别也与 pickle 中是个函数的区别是一样的。

dumps()与 loads()

1
2
3
4
5
6
7
8
9
>>> import pickle
>>> p_dict = {'name':' 张三 ' , 'age':30 , 'isMarried':False} # 定义一个字典
>>> import json
>>> p_dict = {'name':' 张三 ' , 'age':30 , 'isMarried':False} # 定义一个字典
>>> p_str = json.dumps(p_dict)
>>> type(p_str)
<class 'str'>
>>> p_str
'{"name": "\\u5f20\\u4e09", "age": 30, "isMarried": false}'

可以看到,json 序列化之后得到的是 json 格式字符串,但上述 json 字符串中,中文部分内容显示为了“乱码”。怎么办呢?json 的 dumps()函数(dump()函数也有)中提供了一个 ensure_ascii 参数,将该参数的值设置为 False,可令序列化后中文依然正常显示。

1
2
3
>>> p_str2 = json.dumps(p_dict, ensure_ascii=False)
>>> p_str2
'{"name": " 张三 ", "age": 30, "isMarried": false}'

接着上面的内容进行反序列化:

1
2
3
4
5
6
>>> p1 = json.loads(p_str)
>>> p1
{'name': ' 张三 ', 'age': 30, 'isMarried': False}
>>> p2 = json.loads(p_str)
>>> p2
{'name': ' 张三 ', 'age': 30, 'isMarried': False}

dump()与 load()

1
2
3
4
5
>>> import json
>>> p_dict = {'name':' 张三 ' , 'age':30 , 'isMarried':False} # 定义一个字典
>>> file = open('d:/mydump.txt' , 'w')
>>> json.dump(p_dict , file)
>>> file.close()

当然,你也可以加上 ensure_ascii 这一参数,并将其值设置为 False,这样你打开 mydump.txt 文件里面的中文就能正常显示。(执行完代码之后,本地会有一个 mydump.txt 文件,诸位可以验证该内容)

1
2
3
>>> file = open('d:/mydump.txt' , 'w')
>>> json.dump(p_dict , file , ensure_ascii=False)
>>> file.close()

继续反序列化:

1
2
3
4
5
6
7
>>> file = open('d:/mydump.txt' , 'r')
>>> p = json.load(file)
>>> file.close()
>>> type(p)
<class 'dict'>
>>> p
{'name': ' 张三 ', 'age': 30, 'isMarried': False}

通过上面内容,pickle 和 json 模块关于序列化与反序列化的操作就介绍完了。我们可以发现,pickle 与 json 两个模块无论是在函数名,还是在功能上,都是机器相似的。既然这样,有了 pickle 模块,为什么还有 json 模块的诞生呢?接下来来说说 pickle 与 json 模块的区别。

pickle 模块与 json 模块的区别

(1)pickle 模块用于 Python 语言特有的类型和用户自定义类型与 Python 基本数据类型之间的转换

json 模块用于字符串和 python 数据类型间进行转换。如下所示,我们自定义一个 Person 类,分别用 pickle 和 json 进行序列化:

1
2
3
4
5
6
>>> class Person:
def __init__(self , name , age , isMarried):
self.name = name
self.age = age
self.isMarried = isMarried
>>> p = Person(' 张三 ' , 30 , False)

使用 pickle 模块进行序列化与反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
>>> p = Person(' 张三 ' , 30 , False)
>>> import pickle
>>> pp = pickle.dumps(p)
>>> type(pp)
<class 'bytes'>
>>> pp
b'\x80\x03c__main__\nPerson\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x06\x00\x00\x00\xe5\xbc\xa0\xe4\xb8\x89q\x04X\x03\x00\x00\x00ageq\x05K\x1eX\t\x00\x00\x00isMarriedq\x06\x89ub.'
>>> p2 = pickle.loads(pp)
>>> type(p2)
<class '__main__.Person'>
>>> p2.name
' 张三 '

甚至 pickle 模块还能够对 Peron 本身进行序列化:

1
2
3
4
5
6
>>> per = pickle.dumps(Person)
>>> per
b'\x80\x03c__main__\nPerson\nq\x00.'
>>> per2 = pickle.loads(per)
>>> per2
<class '__main__.Person'>

如果用 json 对 Person 实例对象进行序列化,就会报错:

1
2
3
4
5
6
7
8
>>> import json
>>> p = Person(' 张三 ' , 30 , False)
>>> json.dumps(p)
Traceback (most recent call last):
File "<pyshell#49>", line 1, in <module>
json.dumps(p)
……
TypeError: Object of type 'Person' is not JSON serializable

如果非要用 json 对 Person 对象进行序列化,必须先定义一个将 Person 对象转化为字典(dict)的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> def person2dict(per):
return {
'name':per.name ,
'age':per.age ,
'isMarried':per.isMarried
}
>>> p3 = json.dumps(p , default=person2dict)
>>> type(p3)
<class 'str'>
>>> p3
'{"name": "\\u5f20\\u4e09", "age": 30, "isMarried": false}'
>>> p3 = json.dumps(p , default=person2dict , ensure_ascii=False)
>>> type(p3)
<class 'str'>
>>> p3
'{"name": " 张三 ", "age": 30, "isMarried": false}'

当然,也不能直接进行反序列化,不然也只会得到一个字典:

1
2
3
4
5
>>> p4 = json.loads(p3)
>>> type(p4)
<class 'dict'>
>>> p4
{'name': ' 张三 ', 'age': 30, 'isMarried': False}

此时,也要定义一个将字典转换为 Person 类实例的方法,在进行反序列化:

1
2
3
4
5
6
7
>>> def dict2person(d):
return Person(d['name'],d['age'],d['isMarried'])
>>> p5 = json.loads(p3 , object_hook=dict2person)
>>> type(p5)
<class '__main__.Person'>
>>> p5.name
' 张三 '

(2)pickle 序列化结果为 bites 类型,只适合于 Python 机器之间的交互。

json 序列化结果为 str 类型,能够被多种语言识别,可用于与其他程序设计语言交互。

目前,JSON 格式字符串已经成为网络传输中的一种标准格式,所以在 web 后台开发中通常用 json 模块来序列化而不是 pickle 模块。

JSON 和 Python 内置的数据类型对应如下:

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

总结

  1. 序列化与反序列化是为了解决内存中对象的持久化与传输问题;
  2. Python 中提供了 pickle 和 json 两个模块进行序列化与反序列化;
  3. dumps()和 dump()用于序列化,loads()和 load()用于反序列化;
  4. pickle 模块能序列化任何对象,序列化结果为 bites 类型,只适合于 Python 机器之间交互;

json 模块只能序列化 Python 基本类型,序列化结果为 json 格式字符串,适合不同开发语言之间交互。