1、什么是序列化
序列化指的是把内存的数据类型转换成一种特定的格式内容,此格式的内容可用于存储或者跨平台数据交流使用!
反序列化正好相反,从特定格式转化为内存中的数据类型
特定格式即json格式pickle
比如说str把列表字典转换成字符串,也是序列化的概念
2、为何用序列化
用于存储或跨平台平台用!
先说跨平台,公司内部项目也好,开源项目也罢,很多时候都不是仅由一种编程语言开发出来的!下面是我从GitHub上TensorFlow项目中截图下来的:
如上所示,可能会因为各种原因用不同编程语言编写的代码来实现某一项功能!这时候就需要考虑通用性,如何让Python代码也能在Go平台上被识别!这时候需要一种通用格式来兼容各语言,能够相互间转换数据类型!现在比较主流的是json格式!
再来说玩单机游戏,都知道程序是运行在内存中,你的装备等级技能等等都是一个又一个名称所对应的内存地址;当你因为某些事情需要出门时,游戏数据怎么办?下次玩又要重头来吗?这你能忍?我们需要将当前游戏所有状态保存下来到磁盘或者别的地方去,下次玩的时候再加载回来即可,这种我们叫存档!存档只是针对这款游戏或者说针对它后面的程序的,不涉及到什么跨平台等等,因此不需要考虑跨平台兼容性,可以用自己的专有格式,Python中就有pickle,json关注的是跨平台,但是某些语言专有的数据类型它没招,比如Python的集合Java上就没有对应的类型!因此pickle仅适用于Python平台序列化反序列化!
下面来个Python与json对应关系表
Python | JSON |
---|---|
dict | {} 即object |
list, tuple | [] 即array |
str | string |
int, float | number |
True, Flase | true, flase |
None | null |
3、如何用它们?
3.1、json.dumps序列化
调用json.dumps功能转换为json后,返回的是str。
>>> var = json.dumps(['sanxi', 666, True, {'name':'sanxi'}])
>>> print(var,type(var))
["sanxi", 666, true, {"name": "sanxi"}] <class 'str'>
3.2、json.loads反序列化
>>> var = json.loads(var)
>>> print(var, type(var))
['sanxi', 666, True, {'name': 'sanxi'}] <class 'list'>
3.3、序列化结果到文件
其实dump就是调用一个dumps()将数据转换为json格式,再调用write()将其写入目标文件。
var1 = ['sanxi', 666, True, {'name': 'sanxi'}]
with open('hihi.py', 'wt', encoding='utf-8') as json_file:
json.dump(var1, json_file)
3.4、反序列化文件内容
同上,load先调用read()将文件内容读取出来,再调用loads将其还原为Python数据类型。
with open('hihi.py', 'rt', encoding='utf-8') as load_json:
print(json.load(load_json))
# 执行得到结果
['sanxi', 666, True, {'name': 'sanxi'}]
3.5、json注意事项
- json格式是为了兼容所有编程语言通用的数据类型,不能识别某语言专有数据类型,比如Python的集合!
- json字符串必须用双引号,没有单引号!
- json.loads可以转化bytes类型,除了Python3.5!
- 中文序列化为json格式时,是被保存成Unicode格式而不是直观的中文字符
>>> var1 = json.dumps('你好')
>>> print(var1)
"\u4f60\u597d"
>>> json.loads(var1)
'你好'
4、pickle的序列化与反序列化
用法与json模块一致,但是因为局限于Python,且不同版本间可能存在不兼容情况,因此很少用到!
4.1、pickle与json区别
json转换数据类型为str,而pickle转换数据为bytes
4.2、序列化
>>> var2 = pickle.dumps(666)
>>> print(var2, type(var2))
b'\x80\x04\x95\x04\x00\x00\x00\x00\x00\x00\x00M\x9a\x02.' <class 'bytes'>
4.3、反序列化
>>> print(pickle.loads(var2))
666
4.4、序列化到文件
pickle序列化以后存入文件的内容并不能看到,只能使用pickle反序列化以后,才能进行打印显示!
var2 = [666, False, None]
with open('hihi.py', 'wb') as load_pickle:
pickle.dump(var2, load_pickle)
4.5、反序列化文件内容
with open('hihi.py', 'rb') as load_pickle:
print(pickle.load(load_pickle))
# 执行得到结果
[666, False, None]
4.6、pickle的Python2、3兼容问题
pickle在Python3默认protocol为4,而Python2只支持2,所以用Python3写的代码若需要兼容Python,需要手动指定protocol为2
with open('hihi.py', 'wb') as load_pickle:
print(pickle.dump('nihao', load_pickle, protocol=2))
5、猴子补丁
monkey patch,由来网上有多个版本,不在本次讨论范畴!实际作用就是在模块运行时替换掉模块源代码,怎么到的呢?在模块被导入时产生了名称空间,通过别名方式篡改名称指向另外的函数,这样子当代码运行时,看似是原来的样子,实际上运行的是另外一个模块的代码!说这有点绕,还是用示例来说明!
之前我们说过在import导入时,名称空间就已经定义好了,所以像import ujson as json这种方式是行不通的,因为这是两个不同的空间!
但是我们导入后通过调用json模块具体的功能再赋予别名可就不一样了,这是可以影响到的,而且Python模块特点是当第一次导完后产生名称空间,后续的导入将直接使用第一次的名称空间,这样后续所有用到json模块指定功能的地方都将直接使用被偷梁换柱的ujson!
# 代码拷贝自网络
import json
import ujson
def monkey_patch_json():
json.__name__ = ujson.__name__
json.dump = ujson.dump
json.load = json.load
monkey_patch_json()