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对象之间,需要有逗号分隔,当格式不正确时会报这个错误

posted @   ShineLe  阅读(91)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示