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进行序列化。
  • lsittuple类型会转化为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类型的输出可以看出1123都为字符串类型。并且显示的每一字符都是真实的数据,包括上面的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

 

posted @ 2020-06-09 01:28  没有想象力  阅读(349)  评论(0编辑  收藏  举报