Python:序列化 pickle JSON
0、常用
本节为python中json的常见用法总结,初学者请从下一节开始开
Json在Python中的表现形式为dict、list两种
从dict、list转为json:
import json d=dict(name='Bob',age=20,score=90) station=json.dumps(d) '{"name": "Bob", "age": 20, "score": 90}' #写入 with open('station.json','w') as f: json.dump(station,f)
从json转为dict、list:
#station.json
{ "invalidId": [6, 8, 14], "rate": [0.84, 0.96 , 1.12], "threshold":15, "tip": "备注" }
#该文件的读取和使用: with open('station.json','r') as file: station=json.load(file) invalidId=station['invalidId'] rate=station['rate']
编码写入:ensure-ascii
with open('station.json','w') as f:
json.dump(station,f)
如果写入后显示的是unicode字符(以/u开头的十六进制字符)
那么写入时应该写成以下形式:
with open('station.json','w',encoding='utf-8') as f:
json.dump(station,f,ensure-ascii=False)
编码读取
如果写入时用到了encoding,那么读取时也要用encoding
with open('station.json','r',encoding='utf-8') as f:
s=json.dump(f)
否则会报错UnicodeDecodeError
换行写入:indent
默认情况下写入json文件的内容会放在同一行,如果要换行表示,可以加入参数indent:
with open('station.json','w',encoding='utf-8') as f: json.dump(station,f,ensure-ascii=False,indent=4)
1、序列化
在程序运行的过程中,所有的变量都储存在内存中,例如定义一个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=score S=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实例对象。
Json文件样式 staion.json:
{ "invalidId": [6, 8, 14], "rate": [0.84, 0.96 , 1.12], "threshold":15, "tip": "备注" }
该文件的读取和使用:
with open('station.json','r') as file: station=json.load(file) invalidId=station['invalidId'] rate=station['rate']
写入
with open('station.json','w') as f: json.dump(station,f)
如果写入后显示的是unicode字符(以/u开头的十六进制字符)
那么写入时应该写成以下形式:
with open('station.json','w',encoding='utf-8') as f: json.dump(station,f,ensure-ascii=False)
读取
如果写入时用到了encoding,那么读取时也要用encoding
with open('station.json','r',encoding='utf-8') as f: s=json.dump(f)
否则会报错UnicodeDecodeError
总结
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
5、异常、问题
1)np.NaN会被替换为float类型的nan,需要加一个判断语句进行回转
2)json.decoder.JSONDecodeError: Expecting ‘,‘
每条json对象之间,需要有逗号分隔,当格式不正确时会报这个错误
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性