
正文
Python:序列化 pickle JSON
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
序列化
在程序运行的过程中,所有的变量都储存在内存中,例如定义一个dict
d=dict(name='Bob',age=20,score=88)
可以随时修改变量,比如把name修改为'Bill',但是一旦程序结束,变量所占用的内存就被OS完全回收。如果没有把修改后的'Bill'存储到磁盘上,下次重新运行程序,变量又会被初始化为'Bob'。
序列化
把变量从内存中变为可存储或传输的过程称为序列化,在Python中称为pickling,其他语言中称为serilization、marshalling、flattening等等。
序列化后,可以把序列化后的内容写入磁盘,或者通过网络传输到其他机器上。
反序列化:把变量内容从序列化的对象重新读到内存中,unpickling。
Python的序列化在pickle模块中,举例,我们想把之前创建的Dict对象序列化并写入文件中:
import pickle
d=dict(name='Bob',age=20,score=88)
pickle.dumps(d)
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x03\x00\x00\x00Bobq\x02X\x03\x00\x00\x00ageq\x03K\x14X\x05\x00\x00\x00scoreq\x04KXu.'
pickle.dumps()方法把任意对象序列化为一个bytes,然后就可以把这个bytes写入文件。
或者用另一个方法pickle.dump()直接把对象序列化后写入一个file-like Object:
f=open('dump.txt','wb')
pickle.dump(d,f)
f.close()
我们直接查看dump.txt文件时,看到的是一堆混乱的内容,但这些都是Python保存的对象内部信息。
之后我们再进行反序列化,把对象从磁盘读到内存,可以先把内容读到一个bytes,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.load()方法直接从一个file-like Object中直接反序列化出对象。
#pickle.load() 参数是file-like Object
with open('dump.txt','rb') as f:
d2=pickle.load(f)d2
{'name': 'Bob', 'age': 20, 'score': 88}#pickle.loads() 参数是二进制Data
with open('dump.txt','rb') as f:
d=f.read()
N=pickle.loads(d)N
{'name': 'Bob', 'age': 20, 'score': 88}
Python的序列化库pickle可以保存数据的类型,例如将一个dict进行序列化后,再进行反序列化,得到的仍是一个dict(这一点很重要),且其值与之前的dict相同。
存在的问题:python的Pickle只能作用于Python,不能用于其他语言,甚至Python不同的版本也不兼容。因此,只能用pickle保存那些不重要的内容,这样不能成功的反序列化也没关系。
JSON
上文结尾提到,pickle不能用于python之外的语言序列化对象,为了解决这一问题,可以把对象序列化为标准格式,比如XML;但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,而且比XML快,并且可以直接在Web页面中读取,非常方便。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型的对应情况如下:
| JSON | Python |
| {} | dict |
| [] | list |
| 'String' | str |
| 123.456 | int或float |
| true/false | True/Flase |
| null | None |
Python → Json
Python内置的json模块提供了完善的Python对象到JSON格式的转换。举例,如何把Python对象转化为JSON:
import json
d=dict(name='Bob',age=20,score=90)
json.dumps(d)'{"name": "Bob", "age": 20, "score": 90}'
json.dumps()返回一个str,其中的内容就是标准JSON。同理,json.dump()方法可以直接把JSON写入一个file-like Object。
with open('Json.txt','w') as f:
json.dump(d,f)with open('Json.txt','r') as f:
print(f.read())
{"name": "Bob", "age": 20, "score": 90}
Json → Python
要把JSON反序列化为Python对象,则要用loads()或者load()方法,前者把JSON的字符串反序列化,后者直接从file-like Object中读取字符串并且反序列化
#json.loads() 从JSON字符串中反序列化
S=json.dumps(d)
json.loads(S)
{'name': 'Bob', 'age': 20, 'score': 90}#json.load() 从file-like Object中反序列化
with open('Json.txt','r') as f:
S2=json.load(f)S2
{'name': 'Bob', 'age': 20, 'score': 90}
由于JSON标准规定JSON编码是UTF-8,所以我们总是可以正确地在Python的str与JSON字符串间转换。
JSON复杂对象的序列化
从之前的表格中,可以看出几种常用类型从JSON到Python间格式的变化。
但是并不存在一种class间的变化,即无法将我们自定义的class类化为JSON,如果直接用dumps转化一个class时,会出现TypeError的错误
import json
class Student():
def __init__(self,name,age,score):
self.name=name
self.age=age
self.score=scoreS=Student('Bob',20,90)
print(json.dumps(S))TypeError: Object of type Student is not JSON serializable
这是因为,默认情况下,dumps不知道如何将Student实例变为一个JSON的{}对象
解决方法:
通过修改dumps()的参数default,我们需要为要进行转化的对象专门写一个转换函数,再把函数作为default参数传进去:
def Student2dict(S):
return {
'name':S.name,
'age':S.age,
'score':S.score
}
S=Student('Bob',20,90)
print(json.dumps(S,default=Student2dict))
但是该转换函数,只对Student对象起作用,对于其他的类对象,同样不起作用。
一个通用、简便的任意class到JSON转换的语句为:
print(json.dumps(S,default=lambda obj:obj.__dict__))
因为class通常都有一个__dict__属性,它是一个dict,用来存储实例变量。当然也有例外,比如定义了__slot__的class(__slot__属性:规定了类只能有的属性)。
JSON逆序列化
与dumps类似,需要手写一个从JSON到class的方法,并将其作为json.loads()方法的object_hook参数传入。
def Dict2Student(D):
return Student(D['name'],D['age'],D['score'])
S2=json.loads(D,object_hook=Dict2Student)
print(S2)
<__main__.Student object at 0x00000165B0EF8CC8>
打印出的是反序列化的Student实例对象。
总结:
1、几个方法的用法:
d=json.dumps(S) #json序列化
json.dump(f,S) #json序列化 将S序列化保存到file-like Object f中
S=json.loads(d) #json逆序列化
S=json.load(f) #从file-like Object f中逆序列化数据到S中
关于load方法,不是json.load(f,S) 而是 S=json.load(f)
2、修改json.dumps方法的default参数,一种简单、通用的class对象序列化为dict的方法:
D=json.dumps(S,default=lambda obj:obj.__dict__)
3、逆序列化时,要修改的loads方法的参数为object_hook
4、如果想要正常显示中文,需要在dump时添加参数ensure_ascii=False








