序列化

目录

1 序列化

2 pickle

2.1 pickle.dumps(obj) — 把 obj 对象序列化后以 bytes 对象返回,不写入文件

2.2 pickle.loads(bytes_object) — 从 bytes 对象中读取一个反序列化对象,并返回其重组后的对象

2.3 pickle.dump(obj , file) — 序列化对象,并将结果数据流写入到文件对象中

2.4 pickle.load(file) — 反序列化对象,将文件中的数据解析为一个Python对象

3 json

3.1 json格式简介

3.2 json模块简单使用

3.3 JSON进阶

3.4 json.dump使用时报错int64 is not jason serializable

3.4.1 为什么JSON不支持 int64 类型?

3.4.2 解决方法

4 小结


1 序列化

        在程序运行的过程中,所有的变量都是在内存中,比如,定义一个dict:

d = dict(name='Bob', age=20, score=88)

        可以随时修改变量,比如把name改成'Bill',但是一旦程序结束,变量所占用的内存就被操作系统全部回收。如果没有把修改后的'Bill'存储到磁 盘上,下次重新运行程序,变量又被初始化为'Bob'。

        我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serializationmarshallingflattening等等, 都是一个意思。 序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling

  • 序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
  • 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

2 pickle

        Python提供了pickle模块来实现序列化。

  • pickle 序列化后的数据,可读性差,人一般无法识别;
  • pickle 模块只能在 python 中使用,且只支持同版本,不能跨平台使用;
  • Python 中所有的数据类型(列表,字典,集合,类等)都可以用 pickle 来序列化。
  • 需导入 pickle 模块 — import pickle

        pickle模块常用的方法有:dumpsloadsdumpload。

2.1 pickle.dumps(obj) — 把 obj 对象序列化后以 bytes 对象返回,不写入文件

下面代码分别对列表l1、元组t1、字典dic1进行序列化操作,打印后可以看到结果是一堆二进制乱码。代码如下所示:

import pickle
l1=[1,2,3,4,5]
t1=(1,2,3,4,5)
dic1={"k1":"v1","k2":"v2","k3":"v3"}
 
res_l1=pickle.dumps(l1)
res_t1=pickle.dumps(t1)
res_dic=pickle.dumps(dic1)
 
print(res_l1)
print(res_t1)
print(res_dic)
 
对数据进行序列化操作后,打印数据得到结果为:
b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04K\x05e.'
b'\x80\x03(K\x01K\x02K\x03K\x04K\x05tq\x00.'
b'\x80\x03}q\x00(X\x02\x00\x00\x00k1q\x01X\x02\x00\x00\x00v1q\x02X\x02\x00\x00\x00k2q\x03X\x02\x00\x00\x00v2q\x04X\x02\x00\x00\x00k3q\x05X\x02\x00\x00\x00v3q\x06u.'

2.2 pickle.loads(bytes_object) — 从 bytes 对象中读取一个反序列化对象,并返回其重组后的对象

        下面代码是先分别对列表l1、元组t1、字典dic1进行序列化,然后再对序列化后的对象进行反序列化操作,打印后可以看到序列化之前是什么类型的数据,反序列化后其数据类型不变。代码如下所示:

import pickle
l1=[1,2,3,4,5]
t1=(1,2,3,4,5)
dic1={"k1":"v1","k2":"v2","k3":"v3"}
 
res_l1=pickle.dumps(l1)
res_t1=pickle.dumps(t1)
res_dic=pickle.dumps(dic1)
 
print(pickle.loads(res_l1),type(pickle.loads(res_l1)))
print(pickle.loads(res_t1),type(pickle.loads(res_t1)))
print(pickle.loads(res_dic),type(pickle.loads(res_dic)))
 
打印序列化后的数据及其类型,得到结果为:
[1, 2, 3, 4, 5] <class 'list'>
(1, 2, 3, 4, 5) <class 'tuple'>
{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'} <class 'dict'>

2.3 pickle.dump(obj , file) — 序列化对象,并将结果数据流写入到文件对象中

        下面代码分别把 列表l1、元组t1、字典dic1 进行序列化操作,并保存到一个文件中以实现永久保存。代码如下所示:

import pickle
l1=[1,2,3,4,5]
t1=(1,2,3,4,5)
dic1={"k1":"v1","k2":"v2","k3":"v3"}
#把列表l1序列化进一个文件f1中
with open("f1","wb") as f:
    pickle.dump(l1,f)
    pickle.dump(t1,f)
    pickle.dump(dic1,f)
 
序列化后在当前目录下生成一个f1文件,打开文件f1可以看到一堆乱码,如下所示:
€]q (KKKKKe.€(KKKKKtq .€}q (X   k1qX   v1qX   k2qX   v2qX   k3qX   v3qu.

2.4 pickle.load(file) — 反序列化对象,将文件中的数据解析为一个Python对象

        下面代码是先对列表l2进行序列化并保存到文件f2中,然后再对文件f2进行反序列化,得到文件f2里保存的数据。

import pickle
l2=[1,2,3,4,5,6]
#把列表l2序列化进一个文件f2中
with open("f2","wb") as f:
    pickle.dump(l1,f)
with open("f2","rb") as f:
    res=pickle.load(f)
    print(res,type(res))
    
反序列化之后,打印数据及其类型可以看到:
[1, 2, 3, 4, 5, 6] <class 'list'>

        Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。

        pickle可以序列化python的任何数据结构,包括一个类,一个对象,会有安全问题。

        Python Pickle反序列化带来的安全问题:https://blog.csdn.net/weixin_34245082/article/details/87982254

3 json

3.1 json格式简介

        json(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集 。 JSON采用完全独立于语言的文本格式,但是也使用了类似于 C语言 家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使 JSON 成为理想的数据交换语言。 

JSON支持两种数据结构存在:

  • 对象(object):一个对象包含一系列非排序的名称/值对(pair),一个对象以{开始,并以}结束。每个名称/值对之间使用 : 分割。
  • 数组 (array):一个数组是一个值(value)的集合,一个数组以 [ 开始,并以 ] 结束。数组成员之间使用 , 分割。 具体的格式如下:[value1, value2, value3]
  • 名称/值(pair):名称和值之间使用 : 隔开,格式如下:{name:value}。名称必须是字符串类型; 值(value)必须是可以是字符串(string),数值(number),对象(object),有序列表(array),或者 false, null, true 的其中一种。

        JSON的格式描述可以参考RFC 4627。

3.2 json模块简单使用

        如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就 是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web 页面中读取,非常方便。

        json更安全,因为它更有限。json文档可以编码的python类型是unicode,int,float,NoneType,bool,list和dict。它们以基本上无关紧要的方式编组/解组,不易受代码注入攻击。

       JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:

 

JSON类型Python类型
{}dict
[]list
"string"str
1234.56int或float
true/falseTrue/False
nullNone

        内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON:

>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'

        dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object

        要把JSON反序列化为Python对象,用 loads() 或者对应的 load() 方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化:

>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}' 
>>> json.loads(json_str) 
{'age': 20, 'score': 88, 'name': 'Bob'} 

        由于JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在Python的str与JSON的字符串之间转换。

函数描述
dumps将 Python数据类型(如字典,列表)编码成 JSON字符串
loads将已编码的 JSON字符串 解码为 Python数据类型(如上)
dumpjson.dump(data,fp),把数据data转换成 JSON str型 存到fp指向的文件(不限于json文件)
loadjson.load(fp),将fp指向的文件内容读取出来,并转换形如Python数据格式的 str 为Python数据格式

 

 

 

 

 

3.3 JSON进阶

        Python的dict对象可以直接序列化为JSON的{},不过,很多时候,我们更喜欢用class表示对象,比如定义Student类,然后序列化:

import json
class Student(object):
def __init__(self, name, age, score):
self.name = name
self.age = age
self.score = score
s = Student('Bob', 20, 88)
print(json.dumps(s))

        运行代码,毫不留情地得到一个  TypeError: Traceback (most recent call last): ... TypeError: is not JSON serializable

        错误的原因是Student对象不是一个可序列化为JSON的对象。 如果连class的实例对象都无法序列化为JSON,这肯定不合理! 别急,我们仔细看看dumps()方法的参数列表,可以发现,除了第一个必须的obj参数外,dumps()方法还提供了一大堆的可选参数:

 https://docs.python.org/3/library/json.html#json.dumps

        这些可选参数就是让我们来定制JSON序列化。前面的代码之所以无法把Student类实例序列化为JSON,是因为默认情况下,dumps()方法不知道如 何将Student实例变为一个JSON的{}对象。 可选参数default就是把任意一个对象变成一个可序列为JSON的对象,我们只需要为Student专门写一个转换函数,再把函数传进去即可:

def student2dict(std): 
    return { 'name': std.name, 'age': std.age, 'score': std.score }

这样,Student实例首先被student2dict()函数转换成dict,然后再被顺利序列化为JSON:

>>> print(json.dumps(s, default=student2dict))
{"age": 20, "name": "Bob", "score": 88}

不过,下次如果遇到一个Teacher类的实例,照样无法序列化为JSON。我们可以偷个懒,把任意class的实例变为dict:

print(json.dumps(s, default=lambda obj: obj.__dict__))

        因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class。 同样的道理,如果我们要把JSON反序列化为一个Student对象实例,loads()方法首先转换出一个dict对象,然后,我们传入的object_hook函数负 责把dict转换为Student实例:

def dict2student(d):
    return Student(d['name'], d['age'], d['score'])

运行结果如下:

>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>

打印出的是反序列化的Student实例对象。

注:如果想要序列化pandas,numpy格式的数据依旧不成功。

3.4 json.dump使用时报错int64 is not jason serializable

3.4.1 为什么JSON不支持 int64 类型?

  1. JSON 是基于 JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集
  2. JSON 支持number 类型

       Javascript的数字存储使用了IEEE 754中规定的双精度浮点数数据类型,而这一数据类型能够安全存储 -(2^53-1) 到 2^53-1 之间的数值(包含边界值)。JSON 是 Javascript 的一个子集,所以它也遵守这个规则。

以下是rfc7159的说明:

Note that when such software is used, numbers that are integers and are in the range [-(2^53)+1, (2^53)-1] are interoperable in the sense that implementations will agree exactly on their numeric values.

这两个边界值可以通过 JavaScript 的 Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER 获取。

        安全存储的意思是指能够准确区分两个不相同的值,比如,253 - 1 是一个安全整数,它能被精确表示,在任何 IEEE-754 舍入模式(rounding mode)下,没有其他整数舍入结果为该整数。作为对比,253 就不是一个安全整数,它能够使用 IEEE-754 表示,但是 253 + 1 不能使用 IEEE-754 直接表示,在就近舍入(round-to-nearest)和向零舍入中,会被舍入为 253。 Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 将得到 true的结果,而这在数学上是错误的。 同样 105308320612483198 === 105308320612483200 结果也是true

        int64 类型的数值范围是 -(2^63-1) 到 2^63-1。使用int64 类型json 对于超出范围的数字,会出现解析错误的情况。

        一个建议:对于大数字来说,使用str 是一个好的选择。或者用类似这样的结构:{"int": 105308320612483198, "int_str": "105308320612483198"}

        在json 中使用的时候 使用 int_str 属性。

         python 中 int 类型值远远超过IEEE 754 中定义的双精度值的范围,所以对于在 python 中使用的json数据,可以使用放心使用 int64 类型(python中的long )。但是如果序列化后的数据要被其它语言的解析器(比如:JavaScript的解析器)解析的时候,就要当心数值是不是超出了安全数的范围。如果超出,这里推荐使用字符串类型来代替数值类型。

3.4.2 解决方法

错误:TypeError: Object of type 'int64' is not JSON serializable (或者float32)

        在使用json格式保存数据时,经常会遇到xxx  is not JSON serializable,也就是无法序列化某些对象格式,使用了numpy时,使用了np的数据格式,写入data后,json.dumps(data)失败,我们可以自己定义对特定类型的对象的序列化,下面看下怎么定义和使用关于np数据类型的自定义。

1 首先,继承json.JSONEncoder,自定义序列化方法。

class NpEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        elif isinstance(obj, np.floating):
            return float(obj)
        elif isinstance(obj, np.ndarray):
            return obj.tolist()
        else:
            return super(NpEncoder, self).default(obj)

2 使用dumps方法(我们可以直接把dict直接序列化为json对象)加上 cls=NpEncoder,data就可以正常序列化了

json.dumps(data, cls=NpEncoder) 

        其实,很简单,自定义一个序列化方法,然后dumps的时候加上cls=NpEncoder

注:pandas中报这个错误也可以使用

4 小结

        Python语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。

        json模块的dumps()和loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机 制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。

 

 

Python教程:http://www.netpowercorp.com/Upload/file/2017-05-18/201705181922116272.pdf

Python Pickle反序列化带来的安全问题:https://blog.csdn.net/weixin_34245082/article/details/87982254

Python基础(13)——pickle模块的详述(dumps、loads、dump、load的用法):https://blog.csdn.net/weixin_43625577/article/details/86699789

pickle,json序列化:https://www.huaweicloud.com/articles/405c74cb88bd640c911aa5c2e2a7b49c.html

pickle --- Python 对象序列化:https://docs.python.org/zh-cn/3.8/library/pickle.html

python pickle模块https://www.cnblogs.com/cobbliu/archive/2012/09/04/2670178.html

为什么json 不能使用 int64类型https://www.cnblogs.com/xiaonian8/p/14041628.html

转json报错:TypeError: Object of type int64 is not JSON serializable:https://blog.csdn.net/u010452967/article/details/106259505/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值