python反序列化

python的序列化和反序列化是什么

python的序列化和反序列化 是将一个类对象向字节流转化从而进行存储 和 传输 然后使用的时候 再将字节流转化回原始的对象的一个过程

我们可以用代码 来展示出这个序列化 和反序列化 的过程

import pickle

class Person():
def __init__(self):
self.age=18
self.name="Pickle"

p=Person()
opcode=pickle.dumps(p)
print(opcode)
#结果如下
#b'\x80\x04\x957\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06Person\x94\x93\x94)\x81\x94}\x94(\x8c\x03age\x94K\x12\x8c\x04name\x94\x8c\x06Pickle\x94ub.'

P=pickle.loads(opcode)
print('The age is:'+str(P.age),'The name is:'+P.name)

#结果如下
#The age is:18 The name is:Pickle

pickle.dumps(obj[, protocol])

pickle.dumps(obj[, protocol])

  • 函数的功能:将obj对象序列化为string形式,而不是存入文件中。

  • obj:想要序列化的obj对象。

  • protocal:如果该项省略,则默认为0。如果为负值或HIGHEST_PROTOCOL,则使用最高的协议版本。

pickle.loads(string)

功能:从string中读出序列化前的obj对象。

string:文件名称。

参数讲解

dump() 与 load() 相比 dumps() 和 loads() 还有另一种能力:dump()函数能一个接着一个地将几个对象序列化存储到同一个文件中,随后调用load()来以同样的顺序反序列化读出这些对象。

pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict")

文件中读取二进制字节流,将其反序列化为一个对象并返回。

pickle.loads(data, *, fix_imports=True, encoding="ASCII", errors="strict")

data中读取二进制字节流,将其反序列化为一个对象并返回。

在其中 我们可以看到 我们对象的属性 name 和 age 和我们所属的类 都已经存储在里面了

首先使用了pickle.dumps()函数将一个Person对象序列化成二进制字节流的形式。

然后使用pickle.loads()将一串二进制字节流反序列化为一个Person对象。

那么反序列化的代码演示如下

import pickle
class People(object):
def __init__(self,name = "fake_s0u1"):
self.name = name

def say(self):
print "Hello ! My friends"

a=People()
c=pickle.dumps(a)
d = pickle.loads(c)
d.say()

其输出就是 hello ! my friends

我们可以看出 与php的序列化 其实是大同小异的

当我们在其反序列化之前 将people删除了 那么我们在运行的过程中就会因为对象在当前的运行环境中 没有找到这个类而报错 从而反序列化失败

能够序列化的对象

在Python的官方文档中,对于能够被序列化的对象类型有详细的描述,如下

  • NoneTrueFalse

  • 整数、浮点数、复数

  • strbytebytearray

  • 只包含可打包对象的集合,包括 tuple、list、set 和 dict

  • 定义在模块顶层的函数(使用 def定义,lambda 函数则不可以)

  • 定义在模块顶层的内置函数

  • 定义在模块顶层的类

  • 某些类实例,这些类的 dict属性值或 getstate() 函数的返回值可以被打包(详情参阅 打包类实例 这一段)

对于不能序列化的类型,如lambda函数,使用pickle模块时则会抛出 PicklingError`` 异常。

1. 序列化过程:

(1)从对象提取所有属性,并将属性转化为名值对

(2)写入对象的类名

(3)写入名值对

2. 反序列化过程:

(1)获取 pickle 输入流

(2)重建属性列表

(3)根据类名创建一个新的对象

(4)将属性复制到新的对象中

python 是如何做到序列化 和 反序列化的

1. 几个重要函数

python为我们提供了两个比较重要的库picklecpickle 后者是底层使用c语言书写 速度是pickle 的1000倍 但是接口相同

2. 什么是pickle

pickle是Python中一个能够序列化和反序列化对象的模块。

和其他语言类似,Python也提供了序列化和反序列化这一功能,其中一个实现模块就是pickle。在Python中,“Pickling” 是将 Python 对象及其所拥有的层次结构转化为一个二进制字节流的过程,也就是我们常说的序列化,而 “unpickling” 是相反的操作,会将字节流转化回一个对象层次结构。

Pickling 将Python对象及其所拥有的层次结构转化为一个二进制字节流

unpickling 将字节流转化回一个对象层次结构

当然在Python 中并不止pickle一个模块能够进行这一操作,更原始的序列化模块如marshal,同样能够完成序列化的任务,不过两者的侧重点并不相同,marshal**存在主要是为了支持 Python 的**.pyc**文件**。现在开发时一般首选pickle。

marshal模块也能进行序列化操作

marshal**存在主要是为了支持 Python 的**.pyc**文件**

开发时一般首选pickle

pickle实际上可以看作一种 独立的语言 ,通过对opcode的编写可以进行Python代码执行、覆盖变量等操作。直接编写的opcode灵活性比使用pickle序列化生成的代码更高,并且有的代码不能通过pickle序列化得到(pickle解析能力大于pickle生成能力)。

pickle实际上可以看作一种 独立的语言

pickle解析能力大于pickle生成能力

既然opcode能够执行Python代码,那自然就免不了安全问题。以下是Python在pickle文档中的警告。

Warning

警告: pickle 模块在接受被错误地构造或者被恶意地构造的数据时不安全。永远不要 unpickle 来自于不受信任的或者未经验证的来源的数据。

序列化

pickle.dump(文件) 
pickle.dumps(字符串)

反序列化

pickle.load(文件)
pickle.loads(字符串)

他的底层 是通过PVM来实现的 即为python虚拟机 它是实现python序列化 和反序列化的最根本的东西

3. PVM的组成

他是由三个部分组成引擎(或者叫指令分析器),栈区、还有一个 Memo (可以称为标签区)

3.1.1.1. 引擎的作用

从头开始读取流中的操作码和参数 并对其进行解释处理 在这个过程中 会改变栈区 和标签区 直到遇到.这个结束符后停止 处理结束之后 会到达栈顶 形成并返回反序列化的对象

3.1.1.2. 栈区的作用

作为流数据处理过程中的暂存区 在不断的进出过程中 完成对数据流的反序列化 并最终在栈上生成反序列化的结果 由python的list 实现

3.1.1.3. 标签区的作用

如同其名 是数据的一个索引 者 标记 由python的dict 实现 为PVM整个生命周期提供存储

这个图片可以比较好的解释

当前用于 pickling 的协议共有 5 种。使用的协议版本越高,读取生成的 pickle 所需的 Python 版本就要越新。

  • v0 版协议是原始的“人类可读”协议,并且向后兼容早期版本的 Python。

  • v1 版协议是较早的二进制格式,它也与早期版本的 Python 兼容。

  • v2 版协议是在 Python 2.3 中引入的。它为存储 new-style class 提供了更高效的机制。欲了解有关第 2 版协议带来的改进,请参阅 PEP 307

  • v3 版协议添加于 Python 3.0。它具有对 bytes`` 对象的显式支持,且无法被 Python 2.x 打开。这是目前默认使用的协议,也是在要求与其他 Python 3 版本兼容时的推荐协议。

  • v4 版协议添加于 Python 3.4。它支持存储非常大的对象,能存储更多种类的对象,还包括一些针对数据格式的优化。有关第 4 版协议带来改进的信息,请参阅 PEP 3154

  • pickle协议是向前兼容的 ,0号版本的字符串可以直接交给pickle.loads(),不用担心引发什么意外。下面我们以V0版本为例,介绍一下常见的opcode

注意opcode的书写规范

(1)操作码是单字节的

(2)带参数的指令用换行符定界