序列化模块

序列化模块

什么是序列化?

序列化就是把对象的信息和状态转化为可以存储或传输的过程, 通俗来讲就是把内存中的对象转换为字符串或字节的过程,在Python中叫pickling。

为什么要序列化?

1、以某种存储形式使自定义对象持久化
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性。
简单来讲就是序列化后可以把对象的信息写入到文件或数据库中,或是通过套接字在网络上传输。
什么是反序列化?
顾名思义就是重新把序列化后的对象恢复重新读取到内存的过程,在python叫unpicking
如何序列化?
python提供了几个序列化模块

json

要在不同语言之间传递对象,就必须要转化为标准通用的格式,json格式就是很好的选择,它本身就是字符串,很容易在网络中传输和被其他语言解析,再加上json简洁清晰,json更是不二的选择。

Python内置的json模块就能方便的把一些Python对象转化为json格式。

 

json模块主要提供了4个方法: 序列化 dump(), dumps(), 反序列化 load(), loads()

json通过dumps()将对象序列化为字符串和loads()将序列化后对象反序列化恢复原本的对象

import json
dic = dict(k1='v1', k2='v2', k3='v3', k4='v4')                  # 创建字典对象
json_dic = json.dumps(dic)                                      # 序列化字典,返回字符串
print('str', str(dic))                                          # 打印调用字符串强转字典的输出结果
print('json', json_dic)                                         # 打印json序列化后的输出结果

json_str = '{"k1": "v1", "k2": "v2", "k3": "v3", "k4": "v4"}'   # 定义一个json字符串
dic2 = json.loads(json_str)                                     # 反序列化json字符串,这里返回字典对象
print(type(dic2), dic2)                                         # 打印转化后的对象类型和对象信息
str {'k2': 'v2', 'k4': 'v4', 'k1': 'v1', 'k3': 'v3'}
json {"k2": "v2", "k4": "v4", "k1": "v1", "k3": "v3"}
<class 'dict'> {'k2': 'v2', 'k4': 'v4', 'k1': 'v1', 'k3': 'v3'}
out

 

json通过dump()将对象序列化为字符串然后写进文件和load()从文件读取内容反序列化恢复原本的对象

import json

dic = dict(k1='v1', k2='v2', k3='v3', k4=[1, 2, 3])         # 创建字典对象
with open('json_test.txt', 'w') as f:                       # 创建一个f文件句柄
    json.dump(dic, f)                                       # load方法将对象直接序列化写进文件
    # json.dump([1, 2, 3, 4], f)

with open('json_test.txt') as f:                            # 创建文件句柄                               
    dic_obj = json.load(f)                                  # 重新从文件反序列化对象返回
print(type(dic_obj), dic_obj)                               # 打印输出结果和类型

但是load方法有缺陷,一次只能读出一个对象,当有多个对象分别序列化到文件中,反序列化就会报错,遇到这种问题一般都现在内存中序列化对象,然后一行一行主动写进文件中,如下所示

import json
dic = dict(age=12, name='小明', score=77)                   # 创建字典对象
dic2 = dict(age=12, name='小红', score=89)                  # 创建字典对象
dic3 = dict(age=14, name='小王', score=91)                  # 创建字典对象

li = [dic, dic2, dic3]
f = open('json_test.txt', 'w')
for dic in li:
    json_str = json.dumps(dic, ensure_ascii=False)          # ensure_ascii=False使输出到文件中是中文,而不是\u xxx
    f.write(json_str + '\n')
f.close()

f = open('json_test.txt')
for json_str in f:
    dic = json.loads(json_str)                              # 写入文件是一行一个对象,读入也按这个规则解析
    print(dic)
View Code
{'age': 12, 'name': '小明', 'score': 77}
{'age': 12, 'name': '小红', 'score': 89}
{'age': 14, 'name': '小王', 'score': 91}
out

json的定制序列化对象

json的序列化只支持很少的数据类型序列化,像自定义的类,集合等就不能序列化,所以要自己定制json序列化的对象。

通过官方文档可以了解json可以自己定制json序列化的对象。下面是dumps的函数原型,有一大堆可选参数。

json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

其中default参数描述是这样的:

If specified, default should be a function that gets called for objects that can’t otherwise be serialized. It should return a JSON encodable version of the object or raise a TypeError. If not specified, TypeError is raised.

大意是default应该是一个函数供不能被序列化的对象调用,而且该函数返回可以序列化为Json的对象或抛出一个异常。

下面是一个应用,把自定义对象转化为JSON可以解析的字典对象

import json


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

    def __repr__(self):
        return '<class Person> name=%s, age=%d, country=%s' % (self.name, self.age, self.country)


def person_to_dict(person):
    return dict(
        name=person.name,
        age=person.age,
        country=person.country
    )


p = Person('小明', 12, '中国')
print(p)
# json_str = json.dumps(p, default=person_to_dict, ensure_ascii=False)  # 也可以直接一个lambda表达式搞定
json_str = json.dumps(p, default=lambda p: dict(name=p.name, age=p.age, country=p.country), ensure_ascii=False)
print(json_str)
View Code
<class Person> name=小明, age=12, country=中国
{"age": 12, "name": "小明", "country": "中国"}
out

pickle

pickle和json一样提供了4个接口序列和反序列化,不过pickle是把Python的任意对象序列化为bytes类型。

import pickle


student = {'name': '小明', 'age': 12}
pickle_str = pickle.dumps(student)      # 序列化为字节类型
print('student:', student)              # 打印student的信息
print('pickle:', pickle_str)            # 打印序列化后的信息
with open('test_pickle.txt', 'wb') as f:
    f.write(pickle_str)                 # 写进文件, 在文件中查看是一堆乱码

with open('test_pickle.txt', 'rb') as f:
    s = pickle.loads(f.read())          # 反序列化

print('student:', s)                    # 数据也成功还原了
student: {'age': 12, 'name': '小明'}
pickle: b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x0cX\x04\x00\x00\x00nameq\x02X\x06\x00\x00\x00\xe5\xb0\x8f\xe6\x98\x8eq\x03u.'
student: {'age': 12, 'name': '小明'}
out

 

pickle也可以直接dump任意对象到文件,然后读出来,而且也没有了json的load的缺陷。

import pickle
import time

t1 = time.localtime()
t2 = time.localtime(1.5e10)
with open('test_pickle.txt', 'wb') as f:    # 因为pickle是二进制写进文件的,所以也要用二进制方式读写
    pickle.dump(t1, f)                      # 任意对象序列化
    pickle.dump(t2, f)

with open('test_pickle.txt', 'rb') as f:
    t1 = pickle.load(f)
    t2 = pickle.load(f)

print(t1)
print(t2)
time.struct_time(tm_year=2018, tm_mon=11, tm_mday=24, tm_hour=10, tm_min=11, tm_sec=15, tm_wday=5, tm_yday=328, tm_isdst=0)
time.struct_time(tm_year=2445, tm_mon=5, tm_mday=1, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=0, tm_yday=121, tm_isdst=0)
out

 

小结: pickle很强大,能序列化任意对象,而json默认只能序列化少数内置对象(需要自己实现定制),但是pickle只能用于python,序列化后的数据别的语言就不能反序列化了,甚至不同版本的python也可能不能兼容。选择用哪种方式序列化对象就看自己的需求了。

 

参考:

官方文档: json, pickle

序列化

廖海峰 序列化

 

posted @ 2018-11-24 10:34  yscl  阅读(147)  评论(0)    收藏  举报