Python之数据序列化(json、pickle、shelve)
一、python类型数据和JSON数据格式互相转换
pthon 中str类型到JSON中转为unicode类型,None转为null,dict对应object
二. 序列化/反序列化
将对象转换为可通过网络传输或可以存储到本地磁盘的数据格式(如:XML、JSON或特定格式的字节串)的过程称为序列化;反之,则称为反序列化。
三.相关模块
本节要介绍的就是Python内置的几个用于进行数据序列化的模块:
模块名称 | 描述 | 提供的api |
---|---|---|
json | 用于实现Python数据类型与通用(json)字符串之间的转换 | dumps()、dump()、loads()、load() |
pickle | 用于实现Python数据类型与Python特定二进制格式之间的转换 | dumps()、dump()、loads()、load() |
shelve | 专门用于将Python数据类型的数据持久化到磁盘,shelve是一个类似dict的对象,操作十分便捷 | open() |
四、json操作
1、json串转成字典:
(1).loads()方法
1 import json#引用json模块 2 res=json.loads(s) 3 print(res)#打印字典 4 print(type(res))#打印res类型 5 print(res.keys())#打印字典的所有Key
- 要先读文件,然后再转换:
1 f=open('stus.json',encoding='utf-8') 2 content=f.read()#使用loads()方法,需要先读文件 3 user_dic=json.loads(content) 4 print(user_dic)
(2).load()方法
1 import json 2 f=open('stus.json',encoding='utf-8') 3 user_dic=json.load(f) 4 print(user_dic)
(3)区别
- loads()传的是字符串,而load()传的是文件对象
- 使用loads()时需要先读文件再使用,而load()则不用
2、字典转成json串:
- 文件里只能写字符串,但可以把字典转成json串,json串是字符串,可以存到文件里
(1).dumps()方法
1 stus={'xiaojun':'123456','xiaohei':'7891','abc':'11111'} 2 #先把字典转成json 3 res2=json.dumps(stus) 4 print(res2)#打印字符串 5 print(type(res2))#打印res2类型
- .dumps()方法:把字典转成json串
- 使用.dumps()方法前,要先打开文件,再写入:
1 stus={'xiaojun':'123456','xiaohei':'7890','lrx':'111111'} 2 res2=json.dumps(stus,indent=8,ensure_ascii=False) 3 print(res2) 4 with open('stus.json','w',encoding='utf-8') as f:#使用.dumps()方法时,要写入 5 f.write(res2)
(2)dump()方法
1 stus={'xiaojun':'123456','xiaohei':'7890','lrx':'111111'} 2 f=open('stus2.json','w',encoding='utf-8') 3 json.dump(stus,f,indent=4,ensure_ascii=False)
(3)区别
- .dump()不需要使用.write()方法,只需要写哪个字典、哪个文件即可;而.dumps()需要使用.write()方法写入
- 如果要把字典写到文件里面的时候,dump()好用;但如果不需要操作文件,或需要把内容存到数据库和Excel,则需要使用dumps()先把字典转成字符串,再写入
(4)dump\dumps参数
- .dumps\dump中使用参数indent,为字符串换行+缩进:
- .dumps\dump中使用参数ensure_ascii,为内容输出为中文:
1 res2=json.dumps(stus,indent=4,ensure_ascii=False)#为False时内容输出显示正常的中文,而不是转码 2 print(res2)
3、不管是dump还是load,带s的都是和字符串相关的,不带s的都是和文件相关的。
五、pickle模块
pickle模块实现了用于对Python对象结构进行 序列化 和 反序列化 的二进制协议,与json模块不同的是pickle模块序列化和反序列化的过程分别叫做 pickling 和 unpickling:
- pickling: 是将Python对象转换为字节流的过程;
- unpickling: 是将字节流二进制文件或字节对象转换回Python对象的过程;
1. pickle模块与json模块对比
- JSON是一种文本序列化格式(它输出的是unicode文件,大多数时候会被编码为utf-8),而pickle是一个二进制序列化格式;
- JOSN是我们可以读懂的数据格式,而pickle是二进制格式,我们无法读懂;
- JSON是与特定的编程语言或系统无关的,且它在Python生态系统之外被广泛使用,而pickle使用的数据格式是特定于Python的;
- 默认情况下,JSON只能表示Python内建数据类型,对于自定义数据类型需要一些额外的工作来完成;pickle可以直接表示大量的Python数据类型,包括自定数据类型(其中,许多是通过巧妙地使用Python内省功能自动实现的;复杂的情况可以通过实现specific object API来解决)
2. pickle模块提供的相关函数
1 pickle模块提供的几个序列化/反序列化的函数与json模块基本一致: 2 3 # 将指定的Python对象通过pickle序列化作为bytes对象返回,而不是将其写入文件 4 dumps(obj, protocol=None, *, fix_imports=True) 5 6 # 将通过pickle序列化后得到的字节对象进行反序列化,转换为Python对象并返回 7 loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict") 8 9 # 将指定的Python对象通过pickle序列化后写入打开的文件对象中,等价于`Pickler(file, protocol).dump(obj)` 10 dump(obj, file, protocol=None, *, fix_imports=True) 11 12 # 从打开的文件对象中读取pickled对象表现形式并返回通过pickle反序列化后得到的Python对象 13 load(file, *, fix_imports=True, encoding="ASCII", errors="strict")
说明: 上面这几个方法参数中,*号后面的参数都是Python 3.x新增的,目的是为了兼容Python 2.x,具体用法请参看官方文档。
3.实例:内置数据类型的序列化/反序列化
1 >>> import pickle 2 >>> 3 >>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)} 4 5 # 序列化 6 >>> var_b = pickle.dumps(var_a) 7 >>> var_b 8 b'\x80\x03}q\x00(X\x01\x00\x00\x00eq\x01K\nX\x01\x00\x00\x00aq\x02X\x03\x00\x00\x00strq\x03X\x01\x00\x00\x00fq\x04]q\x05(K\x01K\x02K\x03eX\x01\x00\x00\x00gq\x06K\x04K\x05K\x06\x87q\x07X\x01\x00\x00\x00bq\x08G@&333333X\x01\x00\x00\x00cq\t\x88X\x01\x00\x00\x00dq\nNu.' 9 10 # 反序列化 11 >>> var_c = pickle.loads(var_b) 12 >>> var_c 13 {'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None}
dump()与load()
1 >>> import pickle 2 >>> 3 >>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)} 4 5 # 持久化到文件 6 >>> with open('pickle.txt', 'wb') as f: 7 ... pickle.dump(var_a, f) 8 ... 9 10 # 从文件中读取数据 11 >>> with open('pickle.txt', 'rb') as f: 12 ... var_b = pickle.load(f) 13 ... 14 >>> var_b 15 {'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None} 16 >>>
说明:
- 默认情况下Python 2.x中pickled后的数据是字符串形式,需要将它转换为字节对象才能被Python 3.x中的pickle.loads()反序列化;Python 3.x中pickling所使用的协议是v3,因此需要在调用pickle.dumps()时指定可选参数protocol为Python 2.x所支持的协议版本(0,1,2),否则pickled后的数据不能被被Python 2.x中的pickle.loads()反序列化;
- Python 3.x中pickle.dump()和pickle.load()方法中指定的文件对象,必须以二进制模式打开,而Python 2.x中可以以二进制模式打开,也可以以文本模式打开
pickle模块可以直接对自定数据类型进行序列化/反序列化操作,无需编写额外的处理函数或类。
1 >>> stu = Student('Tom', 19, 1) 2 >>> print(stu) 3 Student [name: Tom, age: 19, sno: 1] 4 5 # 序列化 6 >>> var_b = pickle.dumps(stu) 7 >>> var_b 8 b'\x80\x03c__main__\nStudent\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Tomq\x04X\x03\x00\x00\x00ageq\x05K\x13X\x03\x00\x00\x00snoq\x06K\x01ub.' 9 10 # 反序列化 11 >>> var_c = pickle.loads(var_b) 12 >>> var_c 13 Student [name: Tom, age: 19, sno: 1] 14 15 # 持久化到文件 16 >>> with open('pickle.txt', 'wb') as f: 17 ... pickle.dump(stu, f) 18 ... 19 20 # 从文件总读取数据 21 >>> with open('pickle.txt', 'rb') as f: 22 ... pickle.load(f) 23 ... 24 Student [name: Tom, age: 19, sno: 1]
六:shelve模块
shelve是一个简单的数据存储方案,类似key-value数据库,可以很方便的保存python对象,其内部是通过pickle协议来实现数据序列化。shelve只有一个open()函数,这个函数用于打开指定的文件(一个持久的字典),然后返回一个shelf对象。shelf是一种持久的、类似字典的对象。它与“dbm”的不同之处在于,其values值可以是任意基本Python对象--pickle模块可以处理的任何数据。这包括大多数类实例、递归数据类型和包含很多共享子对象的对象。keys还是普通的字符串。
open(filename, flag='c', protocol=None, writeback=False)
flag 参数表示打开数据存储文件的格式,可取值与dbm.open()
函数一致:
值 | 描述 |
---|---|
'r' | 以只读模式打开一个已经存在的数据存储文件 |
'w' | 以读写模式打开一个已经存在的数据存储文件 |
'c' | 以读写模式打开一个数据存储文件,如果不存在则创建 |
'n' | 总是创建一个新的、空数据存储文件,并以读写模式打开 |
protocol 参数表示序列化数据所使用的协议版本,默认是pickle v3;
writeback 参数表示是否开启回写功能。
我们可以把shelf对象当dict来使用--存储、更改、查询某个key对应的数据,当操作完成之后,调用shelf对象的close()函数即可。当然,也可以使用上下文管理器(with语句),避免每次都要手动调用close()方法。
实例:内置数据类型操作
1 # 保存数据 2 with shelve.open('student') as db: 3 db['name'] = 'Tom' 4 db['age'] = 19 5 db['hobby'] = ['篮球', '看电影', '弹吉他'] 6 db['other_info'] = {'sno': 1, 'addr': 'xxxx'} 7 8 # 读取数据 9 with shelve.open('student') as db: 10 for key,value in db.items(): 11 print(key, ': ', value)
输出结果:
1 name : Tom 2 age : 19 3 hobby : ['篮球', '看电影', '弹吉他'] 4 other_info : {'sno': 1, 'addr': 'xxxx'}
实例:自定义数据类型操作
1 # 自定义class 2 class Student(object): 3 def __init__(self, name, age, sno): 4 self.name = name 5 self.age = age 6 self.sno = sno 7 8 def __repr__(self): 9 return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno) 10 11 # 保存数据 12 tom = Student('Tom', 19, 1) 13 jerry = Student('Jerry', 17, 2) 14 15 with shelve.open("stu.db") as db: 16 db['Tom'] = tom 17 db['Jerry'] = jerry 18 19 # 读取数据 20 with shelve.open("stu.db") as db: 21 print(db['Tom']) 22 print(db['Jerry']) 23 24 25 输出结果: 26 27 Student [name: Tom, age: 19, sno: 1] 28 Student [name: Jerry, age: 17, sno: 2]
七:总结
1. 对比
json模块常用于编写web接口,将Python数据转换为通用的json格式传递给其它系统或客户端;也可以用于将Python数据保存到本地文件中,缺点是明文保存,保密性差。另外,如果需要保存非内置数据类型需要编写额外的转换函数或自定义类。
pickle模块和shelve模块由于使用其特有的序列化协议,其序列化之后的数据只能被Python识别,因此只能用于Python系统内部。另外,Python 2.x 和 Python
3.x 默认使用的序列化协议也不同,如果需要互相兼容需要在序列化时通过protocol参数指定协议版本。除了上面这些缺点外,pickle模块和shelve模块相对于json模块的优点在于对于自定义数据类型可以直接序列化和反序列化,不需要编写额外的转换函数或类。
shelve模块可以看做是pickle模块的升级版,因为shelve使用的就是pickle的序列化协议,但是shelve比pickle提供的操作方式更加简单、方便。shelve模块相对于其它两个模块在将Python数据持久化到本地磁盘时有一个很明显的优点就是,它允许我们可以像操作dict一样操作被序列化的数据,而不必一次性的保存或读取所有数据。
2. 建议
- 需要与外部系统交互时用json模块;
- 需要将少量、简单Python数据持久化到本地磁盘文件时可以考虑用pickle模块;
- 需要将大量Python数据持久化到本地磁盘文件或需要一些简单的类似数据库的增删改查功能时,可以考虑用shelve模块。
3. 附录
要实现的功能 | 可以使用的api |
---|---|
将Python数据类型转换为(json)字符串 | json.dumps() |
将json字符串转换为Python数据类型 | json.loads() |
将Python数据类型以json形式保存到本地磁盘 | json.dump() |
将本地磁盘文件中的json数据转换为Python数据类型 | json.load() |
将Python数据类型转换为Python特定的二进制格式 | pickle.dumps() |
将Python特定的的二进制格式数据转换为Python数据类型 | pickle.loads() |
将Python数据类型以Python特定的二进制格式保存到本地磁盘 | pickle.dump() |
将本地磁盘文件中的Python特定的二进制格式数据转换为Python数据类型 | pickle.load() |
以类型dict的形式将Python数据类型保存到本地磁盘或读取本地磁盘数据并转换为数据类型 | shelve.open() |