Python的序列化
序列化
程序在内存中运行时候,使用了一些复杂的数据结构,这些数据结构并不是线性的,但我们在储存数据或传输数据时,必须将这些数据按照先后顺序进行发送或者储存。这就需要将内存中的复杂的数据结构就行序列化处理。
当然我们还可以忽略这些数据结构,直接将内存从起始地址到终止地址数据按字节逐一发送或者储存,下次使用这些数据时原封不动的还原到内存中,这些数据将会重现。这种序列化后的内容我们通常称为 `Image`。
Python序列化工具
pickle
`pickle`是Python自己的序列化工具包,可以将Python中的数据进行序列化,保存到文件之中。对于不同数据格式,使用起始标志+ 数据 + 结束标志储存数据,不同的数据类型对应的起始标识和终止标识有所区别, 这使得在数据保存可它原来的数据类型,在下一次还原时,可以按照原数据类型进行还原。
python序列化工具都提供了重要的方法`dumps` 序列化(复杂结构转字符序列)和 `loads`方法(二进制还原数据结构)
import pickle l = {"l": ["a", 1, {123}, {"f":"g"}]} pickle_str = pickle.dumps(l) with open("test", "w") as f: f.write(pickle_str)
使用 `dumps`方法将复杂的字典+列表的数据结构转化为字符序列的形式写入文件中
obj = pickle.loads(pickle_str) print(obj) # obj 对象将被还原为这个字典 # {"l": ["a", 1, {123}, {"f":"g"}]}
当序列化类对象的时候,只会对实例进行序列化,实例化实例字典中的数据,和该实例的类对象信息。在内存中恢复数据时会使用源代码重新创建对象,并使用并使用序列化的数据还原信息。
优点:
- 保存了原来的数据类型,可以序列化复杂的Python对象,包括自定义的实例对象
- Python独有的序列化工具,序列化和反序列化效率高,序列化后长度小
缺点:
- 只支持Python语言,且Python2和 Python3的pickle协议不兼容,使用局限。
json
json(Javascript object Notation) 是一种使用较为广泛,且较为轻量级的数据交换格式,将支持的数据结构知己转化为字符串文本格式,进行数据的传输。 它支持对JavaScript中的几种基本数据类型进行序列化。大部分编程语言提供了兼容这套序列化标准的库实现,用于将各语言自身的数据结构转化成对应的JavaScript数据类型以完成序列化。python 提供json库实现。
json 格式支持的JavaScript的几种数据类型(详见json http://www.json.org/json-zh.html)
Python类型 | json类型 |
---|---|
True | true |
False | false |
None | null |
str | string |
int | integer |
float | float |
list,tuple | array |
dict | object |
- Python中的
set()
类型提供json的序列化支持,序列化时可转未list
或者tuple
进行序列化。 lsit
和tuple
类型会转化为json的array
类型,反序列化时默认会还原为列表类型- 所有的json类型都为字符串格式,包括None,True,False类型转化的结果均为字符串,对应的"null",“ture”,“false”。
json的使用和pickle的使用类似,提供了两个比较重要的 dumps 和 loads方法进行序列化和反序列化。
import json l = {"l": ["a", 1, [123], {"f":"g"}]} # 序列化 json_str = json.dumps(l) print(json_str) # b'{"l": ["a", 1, [123], {"f": "g"}]}' # 反序列化 obj = json.loads(json_str) print(obj) # {'l': ['a', 1, [123], {'f': 'g'}]} <class 'dict'>
json序列化是将所有的数据类型都转为为字符串类型进行储存,上面实例中存int
类型的1
和 123
,序列化后,通过Python的bytes
类型的输出可以看出1
和123
都为字符串类型。并且显示的每一字符都是真实的数据,包括上面的space
,{
}
[
]
都占用一个字节的空间。这种无效的数据占用了大量的空间,浪费磁盘空间和发送数据时的网络资源。并且不支持实例对象的直接实例化。
messagepack
对于json 这种全部使用字符串序列化的方式,messagepack对这种方式进行优化,形成了一种类似于一种 类型编码 (长度编码) 数据1,数据2
数据形式,储存数据1
时可以直接储存为原类型的二进制形式,使用了原来的数据类型,避免了数据类型的转化和使用字符串储存时的空间浪费。反序列化数据时候,根据该数据的类型声明完成对应数据类型的解码即可。
import msgpack l = {"l": ["a", 1, [123], {"f":"g"}]} # 序列化 msgpack_str = msgpack.dumps(l) print(msgpack_str) # b'\x81\xa1l\x94\xa1a\x01\x91{\x81\xa1f\xa1g' # 反序列化 obj = msgpack.loads(msgpack_str) print(obj, type(obj)) # 反序列化后的内容内层字符串为bytes类型,可以指定loads编码还原原数据类型 # {b'l': [b'a', 1, [123], {b'f': b'g'}]} <class 'dict'> obj = msgpack.loads(msgpack_str, encoding="utf_8") print(obj, type(obj)) # 现在内层的字符结果将会进行解码 # {'l': ['a', 1, [123], {'f': 'g'}]} <class 'dict'>
优点:
- 原数据类型储存,避免了类型转换,对于数值类型更节省空间。
- 相比于json减少了的无效字符,和类型转化,时间和空间上性能都更加优秀。
缺点
- msg 和 json相同,不能序列化Python的实例对象。
- 只能序列化一些基本数据类型的数据
比较
import pickle import time import json import msgpack import random def getkey(n): yield from ("".join(random.choices("abcdefghijklmnopqrstuvwxyz", k=4)) for _ in range(n)) value = ["a", "hello",{"b": "world"}] test_data = dict.fromkeys(getkey(1000), value) for name, package in zip(["json", "msgpack", "pickle"], [json, msgpack, pickle]): start = time.time() for _ in range(10000): res_str = package.dumps(test_data) stop = time.time() print(f"{name:<8}序列化时间为\t", stop-start) start = time.time() for _ in range(10000): package.loads(res_str) stop = time.time() print(f"{name:<8}反序列化时间\t", stop-start)
json 序列化时间为 6.5685741901397705 json 反序列化时间 4.880975961685181 msgpack 序列化时间为 3.23630428314209 msgpack 反序列化时间 3.115692377090454 pickle 序列化时间为 0.965388298034668 pickle 反序列化时间 1.2167444229125977
三种序列化数据的长度比较
for name, package in zip(["json", "msgpack", "pickle"], [json, msgpack, pickle]): res_str = package.dumps(test_data) print(f"{name:<8}序列化后长度为\t", len(res_str)) # 结果 # json 序列化后长度为 39920 # msgpack 序列化后长度为 22957 # pickle 序列化后长度为 15276