序列化模块(json、pickle)
一、定义:
什么是序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫序列化。
序列化:字符串、bytes(结果)
序列:列表、元组、字符串、bytes(有序的数据类型)
把其他数据类型转化成字符串、bytes序列化的过程。(序列化)
二、序列化的目的:
1、一某种存储形式使自定义对象持久化。
2、将对象从一个地方传递到另一个地方。(在网络上传输的只能是bytes、能够存储在文件里的只有bytes和str)
3、使程序更具维护性。
比如,我们在python代码中计算的一个数据需要给另外一段程序使用,那我们怎么给? 现在我们能想到的方法就是存在文件里,然后另一个python程序再从文件里读出来。 但是我们都知道,对于文件来说是没有字典这个概念的,所以我们只能将数据转换成字典放到文件中。 你一定会问,将字典转换成一个字符串很简单,就是str(dic)就可以办到了,为什么我们还要学习序列化模块呢? 没错序列化的过程就是从dic 变成str(dic)的过程。现在你可以通过str(dic),将一个名为dic的字典转换成一个字符串, 但是你要怎么把一个字符串转换成字典呢? 聪明的你肯定想到了eval(),如果我们将一个字符串类型的字典str_dic传给eval,就会得到一个返回的字典类型了。 eval()函数十分强大,但是eval是做什么的?e官方demo解释为:将字符串str当成有效的表达式来求值并返回计算结果。 BUT!强大的函数有代价。安全性是其最大的缺点。 想象一下,如果我们从文件中读出的不是一个数据结构,而是一句"删除文件"类似的破坏性语句,那么后果实在不堪设设想。 而使用eval就要担这个风险。 所以,我们并不推荐用eval方法来进行反序列化操作(将str转换成python中的数据结构)
@谨慎使用eval方法。
三、json模块:json模块提供了四个功能:dumps、dump、loads、load
1、dumps和loads:
import json dic = {'k1':'v1','k2':'v2','k3':'v3'} str_dic = json.dumps(dic) #序列化,将一个字典转化成一个字符串。 print(type(str_dic),str_dic) # 结果:<class 'str'> {"k1": "v1", "k2": "v2", "k3": "v3"} #注意:json转换完成后的字符串类型的字典中字符串是由""表示的 dic2 = json.loads(str_dic) #反序列化,将一个字符串格式的字典转换成一个字典 print(type(dic2),dic2) # 结果:<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'} # 注意:要用json.loads方法处理字符串类型的字典必须由""表示 list_dic = [1,["a","b","c"],3,{'k1': 'v1', 'k2': 'v2'}] str_dic = json.dumps(list_dic) #也可以处理嵌套的数据类型 print(type(str_dic),str_dic) # 结果:<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}] list_dic2 = json.loads(str_dic) print(type(list_dic2),list_dic) # 结果:<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
2、dump和load:
import json f = open('json_file',mode='w',encoding="utf-8") dic = {'k1':'v1','k2':'v2','k3':'v3'} json.dump(dic,f) #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件中 f.flush() f.close() f = open('json_file') dic2 = json.load(f) #load方法接收一个文件句柄,直接将文件中的json字符串换成数据结构返回 f.close() print(type(dic2),dic2) # 结果:<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
@ensure_ascii关键字参数
import json f = open('file',mode='w',encoding='utf-8') json.dump({"国籍":"中国"},f) ret = json.dumps({"国籍":"中国"}) f.write(ret+"\n") json.dump({"国籍":"美国"},f,ensure_ascii=False) ret = json.dumps({"国籍":"美国"},ensure_ascii=False) f.write(ret+"\n") f.flush() f.close() #结果: {"\u56fd\u7c4d": "\u4e2d\u56fd"}{"\u56fd\u7c4d": "\u4e2d\u56fd"} {"国籍": "美国"}{"国籍": "美国"}
@json的格式化输出
import json data = {"naem":["张无忌","无忌"],"sex":"男","age":"24"} json_dic = json.dumps(data,sort_keys=True,indent=4,separators=(",",":"),ensure_ascii=False) print(json_dic) # 结果: { "age":"24", "naem":[ "张无忌", "无忌" ], "sex":"男" }
@格式化输出的参数详情:
Serialize obj to a JSON formatted str.(字符串表示的json对象)
Skipkeys:默认值是False,如果dict的keys内的数据不是python的基本类型(str,unicode,int,long,float,bool,None),设置为False时,就会报TypeError的错误。此时设置成True,则会跳过这类key
ensure_ascii:,当它为True的时候,所有非ASCII码字符显示为\uXXXX序列,只需在dump时将ensure_ascii设置为False即可,此时存入json的中文即可正常显示。)
If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse).
If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity).
indent:应该是一个非负的整型,如果是0就是顶格分行显示,如果为空就是一行最紧凑显示,否则会换行且按照indent的数值显示前面的空白分行显示,这样打印出来的json数据也叫pretty-printed json
separators:分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。
default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError.
sort_keys:将数据根据keys的值进行排序。
To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.
3、json的各种问题
import json # 问题1 dic = {1:"v1",2:"v2"} ret = json.dumps(dic) #序列化 print(dic,type(dic)) print(ret,type(ret)) # 结果: # {1: 'v1', 2: 'v2'} <class 'dict'> # {"1": "v1", "2": "v2"} <class 'str'> res = json.loads(ret) #反序列化 print(res,type(res)) # 结果:{'1': 'v1', '2': 'v2'} <class 'dict'> 现在的key值变成字符串了。 # 问题2 dic = {1:[1,2,3],2:(4,5,"abc")} ret = json.dumps(dic) #序列化 print(dic,type(dic)) print(ret,type(ret)) # 结果: # {1: [1, 2, 3], 2: (4, 5, 'abc')} <class 'dict'> # {"1": [1, 2, 3], "2": [4, 5, "abc"]} <class 'str'> res = json.loads(ret) #反序列化 print(res,type(res)) # 结果:{'1': [1, 2, 3], '2': [4, 5, 'abc']} <class 'dict'> # 问题3 s = {1,2,"abc"} json.dumps(s) # 结果:报错(TypeError: Object of type 'set' is not JSON serializable) # 问题4 s = {(1,2,3):"abc"} json.dumps(s) # 结果:报错(TypeError: keys must be a string) # 问题5: dic = {"k1":"v1","k2":"v2"} with open("json_file",mode='a',encoding='utf-8') as f: json.dump(dic,f) json.dump(dic,f) json.dump(dic,f) with open('json_file',mode='r',encoding='utf-8') as f: dic = json.load(f) print(dic.keys()) # 结果:报错(json.decoder.JSONDecodeError: Extra data: line 1 column 25 (char 24)) # 不支持连续的存 取 @json操作文件 import json # 向文件中记录字典 dic = {"key1":"value1","key2":"value2"} ret = json.dumps(dic) #序列化 with open("json_file",mode='a',encoding='utf-8') as f: f.write(ret) # 从文件中读取字典 with open("json_file",mode='r',encoding='utf-8') as f: str_dic = f.read() dic = json.loads(str_dic) print(dic.keys()) # 结果:dict_keys(['key1', 'key2']) #dump和load直接操作文件中的字典 dic = {"key1":"value1","key2":"value2"} with open("json_file",mode='a',encoding='utf-8') as f: json.dump(dic,f) with open("json_file",mode='r',encoding='utf-8') as f: dic = json.load(f) print(dic.keys()) # 结果:dict_keys(['key1', 'key2'])
#把一个一个的字典放到文件中,在一个一个取出来
import json
dic = {"key1":"value1","key2":"value2"}
with open('json_file',mode='w',encoding='utf-8') as f:
str_dic = json.dumps(dic)
f.write(str_dic+"\n")
str_dic = json.dumps(dic)
f.write(str_dic + "\n")
str_dic = json.dumps(dic)
f.write(str_dic + "\n")
with open('json_file',mode='r',encoding='utf-8') as f:
for el in f:
dic = json.loads(el.strip())
print(dic.keys())
# 结果:
# dict_keys(['key1', 'key2'])
# dict_keys(['key1', 'key2'])
# dict_keys(['key1', 'key2'])
@总结:
1、json在所有语言之间都通用,json序列化的数据在python上序列化了那在Java中也可以反序列化。
能够处理的数据类型是非常有限的:字符串、列表、字典、数字 (字典中的key只能是字符串)
2、dumps和loads
在内存中做数据转换:
dumps 数据类型转成字符串-->序列化
loads 字符串转成数据类型-->反序列化
dump和load
直接将数据类型写入文件,直接从文件中读出数据类型
dump 数据类型写入文件-->序列化
load 文件读出数据类型-->反序列化
四、pickle模块:在python中几乎支持所有的数据类型,只能在python中使用
1、dumps的结果只能是字节
import pickle dic = {(1,2,3):{'a','b'},1:'abc'} ret = pickle.dumps(dic) print(ret) #dumps的结果只能是字节 # 结果: # b'\x80\x03}q\x00(K\x01K\x02K\x03\x87q\x01cbuiltins\nset\nq\x02] # q\x03(X\x01\x00\x00\x00aq\x04X\x01\x00\x00\x00bq\x05e\x85q\x06Rq\x07K\x01X\x03\x00\x00\x00abcq\x08u.' res = pickle.loads(ret) print(res) # 结果:{(1, 2, 3): {'a', 'b'}, 1: 'abc'}
2、在和文件操作的时候,需要用rb、wb的模式打开文件
import pickle dic = {(1,2,3):{'a','b'},1:'abc'} with open('pickle_file',mode='wb') as f: pickle.dump(dic,f) with open('pickle_file',mode='rb') as f: ret = pickle.load(f) print(ret,type(ret)) # 结果:{(1, 2, 3): {'b', 'a'}, 1: 'abc'} <class 'dict'>
3、可以多次dump和多次load
import pickle dic1 = {(1,2,3):{'a','b'},1:'abc'} dic2 = {(1,2,3):{'a','b'},2:'abc'} dic3 = {(1,2,3):{'a','b'},3:'abc'} dic4 = {(1,2,3):{'a','b'},4:'abc'} with open('pick_file',mode='wb') as f: pickle.dump(dic1,f) pickle.dump(dic2,f) pickle.dump(dic3,f) pickle.dump(dic4,f) with open('pick_file',mode='rb') as f: ret1 = pickle.load(f) print(ret1,type(ret1)) ret2 = pickle.load(f) print(ret2, type(ret2)) ret3 = pickle.load(f) print(ret3, type(ret3)) ret4 = pickle.load(f) print(ret4, type(ret4)) # 结果: # {(1, 2, 3): {'a', 'b'}, 1: 'abc'} <class 'dict'> # {(1, 2, 3): {'a', 'b'}, 2: 'abc'} <class 'dict'> # {(1, 2, 3): {'a', 'b'}, 3: 'abc'} <class 'dict'> # {(1, 2, 3): {'a', 'b'}, 4: 'abc'} <class 'dict'>
@读取时的异常处理
import pickle
with open('pick_file',mode='rb') as f:
while True:
try:
ret = pickle.load(f)
print(ret,type(ret))
except EOFError:
break
# 结果:
# {(1, 2, 3): {'b', 'a'}, 1: 'abc'} <class 'dict'>
# {(1, 2, 3): {'b', 'a'}, 2: 'abc'} <class 'dict'>
# {(1, 2, 3): {'b', 'a'}, 3: 'abc'} <class 'dict'>
# {(1, 2, 3): {'b', 'a'}, 4: 'abc'} <class 'dict'>