Python序列化和反序列化

本文介绍Python序列化和反序列化。

序列化和反序列化的概念

把对象转换为字节序列的过程称为对象的序列化;反之,把字节序列恢复为对象的过程称为对象的反序列化。 序列化后可以转换为二进制,xml, json等。


对象的序列化主要用途有2个:
1、 把对象的字节序列永久的保存在硬盘上,通常保存在一个文件中

很多的应用中,需要对一些对象进行序列化,让他们离开内存空间,入住物理硬盘,以便长期的保存。比如最常见的是web服务器的session对象,当10万用户并发访问,就会在内存中出现10万个session对象,内存可能会吃不消。 因此可以把session对象序列化到硬盘中,需要用的时候,再把硬盘中对象还原到内存中。

2、 在网络上传送对象的字节序列。

当2个进程进行远程通讯时,彼此发送各种类型的数据。无论是哪一种数据,都会以二进制序列的形式再网络上传输。发送方把数据序列化为字节序列,接收方再把字节序列回复为对象形式。

再举一个例子,游戏有存档的功能。游戏的数据都是存在内存中,当关机以后,内存的的东西会消失的,但是存档之后就不会,因为是吧游戏的数据序列化到了硬盘中。这就是对象的持久化!

Python如何进行序列化

Python提供了2个模块进行序列化:pickle和json

pickle

pickle是将对象序列为bytes字节,不能和其他的编程语言进行交互,是python独有的。
pickle提供了4个方法:dumps,dump, load, loads。 dumps和dump是序列化方法,loads和load是反序列方法。 py3中已经不存在cPickle模块,在py3中改为的是Pickle。


dumps 和 dump 的区别在于:dumps 只接受一个参数即序列化的对象;dump 可以接受2个参数,一个是序列化的对象,另一个是需要写入的文件。

dumps: 序列化一个对象

In [1]: import pickle

In [2]: d = [1, 2, 3]

In [3]: pickle.dumps(d)   # 序列化方法
Out[3]: b'\x80\x03]q\x00(K\x01K\x02K\x03e.'

In [4]: temp = pickle.dumps(d)

In [5]: temp
Out[5]: b'\x80\x03]q\x00(K\x01K\x02K\x03e.'

In [6]: type(temp)
Out[6]: bytes

dump: 序列化到一个文件中

import pickle

# 序列化对象
f = open('file.txt',  'wb')
d = [1, 3, 5]
pickle.dump(d, f)  # 传递序列化对象和要存储的文件名
f.close()

# 读取序列化后的字节
f = open('file.txt', 'rb')
print(f.read())


loads是把对象从硬盘中读取到内存中,显示读取到bytes,用loads方法反序列化出对象。
load方法是直接反序列化一个文件。

loads: 反序列化字节

In [41]: g = [2222]

In [42]: r = pickle.dumps(g)

In [43]: r
Out[43]: b'\x80\x03]q\x00M\xae\x08a.'

In [44]: pickle.loads(r)
Out[44]: [2222] 

load: 反序列一个文件

In [45]: l = [1,2,2,3,4,6666666]

In [46]: f= open('file.txt', 'wb')

In [47]: pickle.dump(l, f)

In [48]: f.close()

In [49]: f = open('file.txt', 'rb')

In [50]: m = pickle.load(f)

In [51]: f.close()

In [52]: m
Out[52]: [1, 2, 2, 3, 4, 6666666]

以上是对一个变量进行序列化,同样可以对一个类以及实例对象进行序列化。

# coding=utf8
import pickle

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def hello(self):
        print('{name} is {age} years old'.format(name=self.name, age=self.age))

# 将类本身存储在变量中进行序列化
class_Person = pickle.dumps(Person)

# 反序列回这个类
person_temp = pickle.loads(class_Person)

print(person_temp)

# 验证反序列回来的是否就是我们创建的哪个类
if person_temp is Person:
    print('ok')
# 事实证明他就是的!

# 将实例对象进行序列化
p_obj = Person('tom', 22)
temp = pickle.dumps(p_obj)

p = pickle.loads(temp)

p.hello()
# 将实例对象进行序列化后再反序列化回来的对象依然是可以调用实例方法。

同样,是可以使用dump和load方法将序列化后的对象存在文件中的。



Json

pickle是一段二进制,而json表示出来是一个字符串,具有更好的可读性,是可以被各种语言读取的,也就方便了和其他语言间的传递。json是比xml快的,也能够在web页面读取,所以首先json。

json中同样也是上述的4个方法:序列化dump和dumps,反序列化load和loads。

序列化

上代码:

class Person(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def hello(self):
        print('{name} is {age} years old'.format(name=self.name, age=self.age))                    

同样是上面的类。使用json序列化,直接进行序列化是会报错的:

with open('a.json', 'w', encoding='utf8') as f:
    json.dump(p, f)  # TypeError: Object of type 'Person' is not JSON serializable

此时dump需要一个参数default,这个参数接受一个函数。这个函数就是将对象转换为字典的。

def d(person):
    return {'name': person.name,
            'age': person.age}

将d函数设置为default:

with open('a.json', 'w', encoding='utf8') as f:
    json.dump(p, f, default=d)

每次都要写一个这样的函数真的很累,所以可以使用对象实例中的__dict__即可。

with open('a.json', 'w', encoding='utf8') as f:
    json.dump(p, f, default=lambda obj: obj.__dict__)

这样就会序列化成功。

反序列化

读取的时候load一个字典,再转回对象就可,同样需要一个蚕食object_hook,该参数接受一个函数,用于将字典转换为对象。

def dictperson(dic):
    return Person(dic['name'], dic['age'])   

# 进行反序列化
with open('a.json', encoding='utf8') as f:
    obj = json.load(f, object_hook=dictperson)   #将上面的函数设置为参数
    print(obj.name, obj.age)
    obj.hello()   # 反序列化回来的对象一样使用

完整代码如下:

def dictperson(dic):
    return Person(dic['name'], dic['age'])

# 序列化
p = Person('tom', 22)
with open('a.json', 'w', encoding='utf8') as f:
    json.dump(p, f, default=lambda obj: obj.__dict__)   # 此处使用了匿名函数,直接调用`__dict__`

# 反序列化
with open('a.json', encoding='utf8') as f:
    obj = json.load(f, object_hook=dictperson)  
    print(obj.__dict__)       
    print(obj.name, obj.age)
    obj.hello()

输出是:

{'name': 'tom', 'age': 22}
tom 22
tom is 22 years old  

序列化时还有一个shelve模块,但是不常用。暂时不总结了。