Skip to content

Latest commit

 

History

History
343 lines (235 loc) · 8.97 KB

406_序列化与反序列化.md

File metadata and controls

343 lines (235 loc) · 8.97 KB

为什么要序列化

  • 内存中的字典、列表、集合和各种对象,如何保存到一个文件当中?
  • 如果是自己定义的类的实例,如何保存到一个文件当中?
  • 如何从文件中读取数据,并让他们在内存中再次恢复成自己对应的类的实例?
  • 例如将97存到文件中,你知道这是str(97)还是int(97)?

基于以上需求,我们要设计一套协议,按照某种规则,把内存的数据保存到文件中。文件是一个字节序列,使用必须把数据转换成字节序列,输出到文件。这就是序列化

反之,从文件的字节序列恢复到内存并且还是原来的类型,这就是反序列化

定义

serialization,序列化

将内存中对象存储下来,把它变成一个个字节,一般就是2进制

deserialization 反序列化

将文件中的一个个字节恢复成内存中的对象,二进制格式转为其他格式

持久化

  • 序列化保存到文件之后才是持久化
  • 可以将数据序列化后持久化,不一定是文件形式,也可也网络传输;也可以将文件中或者网络接收到的字节序列反序列化

pickle库

序列化过程

import pickle

a = 99
b = 'c'
c = [48, 'abc']
d = {"a": "ddd", "b": 1}

with open('D:/Gitee/python/test/ser', 'wb+') as f:
    pickle.dump(a, f)

用notepad++的hex插件打开看下

80 03 4b 63 2e
其中     63 这一位应该是真正的99
其他的都是描述99这个数字的东西

为了验证下我们的猜测,再看下s数字97,是不是显示61

80 03 4b 61 2e

看样子确实是我们想的样子

再来看下60000表示什么

80 03 4d 60 ea

在ipython里看看60000是什么

>>> hex(60000)
'0xea60'
>>>

ea60,看来写的文件是小端模式

其他类型的数据

import pickle

a = 99
b = 'c'
c = [48, 'abc']
d = {"a": "ddd", "b": 1}

with open('D:/Gitee/python/test/ser', 'wb+') as f:
    pickle.dump(a, f)
    pickle.dump(b, f)
    pickle.dump(c, f)
    pickle.dump(d, f)

notepad++ hex模式查看下

80 03 4b 63 2e 80 03 58 01 20 20 20 63 71 20 2e  # ASCII: 数字99 对应16进制为63   字符'c' 对应的16进制为63

80 03 5d 71 20 28 4b 30 58 03 20 20 20 61 62 63  # ASCII: 数字48对应的16进制为30   'abc'对应的16进制显示为616263

71 01 65 2e 80 03 7d 71 20 28 58 01 20 20 20 61  # ASCII:  字符a 的16进制表示为61

71 01 58 03 20 20 20 64 64 64 71 02 58 01 20 20  # ASCII: 字符ddd的16进制表示为646464

20 62 71 03 4b 01 75 2e                          # ASCII: 字符b表示为62   数字1对应的16进制就是01

总结

  • 字符串存入时,需要反查ASCII表,然后将ASCII对应的索引写入文件中

    • '0' -- \0x30
    • '1' -- \0x31
    • 'a' -- \0x61
  • 数字存入时,直接将数值对应的16进制或2进制写入文件,也要注意大小端模式

    • 1 -- \0x01
    • 0 -- \0x00
    • 99 -- \0x63
  • 可以看到,写入字符串c和数字99,都是写入63(16进制),如果没有序列化,怎么区分?

  • 不只是写入磁盘,在内存中,也是如此排序的

反序列化过程

import pickle

a = '99'
aa = 99
b = 'c'
c = [48, 'abc']
d = {"a": "ddd", "b": 1}

with open('D:/Gitee/python/test/ser', 'wb+') as f:
    pickle.dump(a, f)
    pickle.dump(aa, f)
    pickle.dump(b, f)
    pickle.dump(c, f)
    pickle.dump(d, f)

with open('D:/Gitee/python/test/ser', 'rb+') as f1:
    for i in range(5):
        x = pickle.load(f1)
        print(type(x), x)

# 结果如下
<class 'str'> 99
<class 'int'> 99
<class 'str'> c
<class 'list'> [48, 'abc']
<class 'dict'> {'a': 'ddd', 'b': 1}

pickle总结

  • pickle模块只能在Python中使用,python中几乎所有的数据类型(列表,字典,集合,类等)都可以用pickle来序列化

  • pickle.dump(obj, file[, protocol])

    • 序列化对象,并将结果数据流写入到文件对象中。
      • 参数protocol是序列化模式,默认值为0,表示以文本的形式序列化
      • protocol的值还可以是1或2,表示以二进制的形式序列化。
  • pickle.load(file)

    • 反序列化对象。将文件中的数据解析为一个Python对象
  • pickle.dumps()

    • 将对象序列化为str,而不是保存到文件种
  • pickle.loads()

    • 从str中读取字段,并反序列化

序列化应用

一般来说,本地序列化的情况,应用较少,大多数场景下都应用在网络传输中

将数据序列化后通过网络传输到远程节点,远程服务器上的服务将接收到的数据反序列化后,就可以使用了

但是,要注意一点,远程接收端,反序列化时必须有对应的数据类型,否则就会报错,尤其是自定义类,必须远程得有一致的定义

现在,大多数项目,都不是单机的,也不是单服务的,需要多个程序之间配合。需要通过网络将数据传送到其他节点上去,这就需要大量的序列化、反序列化的过程

但是,问题是,python与python之间还可以使用pickle解决序列化和反序列化,如果是跨平台、跨语言、跨协议的,pickle都不太合适,就需要公用的协议,如xml、json、protocol Buffer等

不同的协议,效率不同、学习曲线不同,适用于不同的场景,需要不同的情况去分析

Json

json(JavaScript Object Notation ,JS对象标记)

json是一种轻量级的数据交换格式,它基于ECMAScript(W3c组织制定的JS规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据

http://json.org/

Json 模块提供了四个方法: dumps、dump、loads、load

序列化

import json


a = {'a': [1, 2], 'b':{'c': 'd'}}
x = json.dumps(a)
print(type(a),'>>', type(x), a)

# 结果如下
<class 'dict'> >> <class 'str'> {'a': [1, 2], 'b': {'c': 'd'}}

把json得到的字符串放入js能直接解析

x = {'a': [1, 2], 'b': {'c': 'd'}}
{a: Array(2), b: {…}}
x.a
(2) [1, 2]
x.b
{c: "d"}

显而易见的,x.a这种调用方法明显是对象的操作

反序列化

import json

a = {'a': [1, 2], 'b':{'c': 'd'}}
x = json.dumps(a)
print(type(a),'>>', type(x), a)

d1 = json.loads(x)
print(type(d1), d1)

# 结果如下
<class 'dict'> >> <class 'str'> {'a': [1, 2], 'b': {'c': 'd'}}
<class 'dict'> {'a': [1, 2], 'b': {'c': 'd'}}

json传输效率

import json
import pickle

a = {'a': [1, 2], 'b':{'c': 'd'}}
jsona = json.dumps(a)
picka = pickle.dumps(a)

print("json's length{}\npickle's length{}".format(len(jsona), len(picka)))

# 结果如下
json's length30
pickle's length53

python与json的类型比较

python支持少量的内建数据类型到json类的转换

python类型 json类型
True true
False false
None null
str string
int integer
float float
list array
dict object

常用方法

python类型 json类型
dumps json编码
dump json编码存入文件
loads json解码
load json解码存入文件

总结

  • 一般json的编码的数据很少落地,数据都是通过网络传输,传输的时候,要考虑压缩它
  • 本质上来说它就是个文本,就是个字符串
  • json很简单,几乎所有的编程语言都支持json,所以应用范围十分广泛

MessagePack

MessagePack是一种有效的二进制序列化格式。 它使您可以在JSON之类的多种语言之间交换数据。 但是它更快,更小。 该软件包提供用于读取和写入MessagePack数据的CPython绑定。

MessagePack对字符串的压缩效率大概是json的一半,一般用于python标准库的内部数据传输

用法

pip install msgpack

import json
import pickle
import msgpack

a = {'a': [1, 2], 'b':{'c': 'd'}}
jsona = json.dumps(a)
picka = pickle.dumps(a)
msga = msgpack.dumps(a)

print("json's length{}\npickle's length{}\nmsg's length{}".format(len(jsona), len(picka), len(msga)))

# 结果如下
json's length30
pickle's length53
msg's length13