一篇文章了解Python中常见的序列化操作
0x00marshal
marshal使用的是与Python语言相关但与机器无关的二进制来读写Python对象的。这种二进制的格式也跟Python语言的版本相关,marshal序列化的格式对不同的版本的Python是不兼容的。
marshal一般用于Python内部对象的序列化。
一般地包括:
- 基本类型booleans,integers,floatingpointnumbers,complexnumbers
- 序列集合类型strings,bytes,bytearray,tuple,list,set,frozenset,dictionary
- code对象codeobject
- 其它类型None,Ellipsis,StopIteration
marshal的主要作用是对Python“编译”的.pyc文件读写的支持。这也是marshal对Python版本不兼容的原因。开发者如果要使用序列化/反序列化,那么应该使用pickle模块。
常见的方法
marshal.dump(value,file[,version])
序列化一个对象到文件中
marshal.dumps(value[,version])
序列化一个对象并返回一个bytes对象
marshal.load(file)
从文件中反序列化一个对象
marshal.loads(bytes)
从bytes二进制数据中反序列化一个对象
0x01pickle
pickle模块也能够以二进制的方式对Python对象进行读写。相比marshal提供基本的序列化能力,pickle的序列化应用更加广泛。
pickle序列化后的数据也是与Python语言相关的,即其它语言例如Java无法读取由Python通过pickle序列化的二进制数据。如果要使用与语言无法的序列化那么我们应该使用json。下文将会说明。
能被pickle序列化的数据类型有:
- None,True,andFalse
- integers,floatingpointnumbers,complexnumbers
- strings,bytes,bytearrays
- tuples,lists,sets,anddictionaries以及包含可以被pickle序列化对象
- 在模块顶层定义的函数对象(使用def定义的,而不是lambda表达式)
- 在模块顶层定义内置函数
- 在模式顶层定义的类
- 一个类的__dict__包含了可序列化的对象或__getstate__()方法返回了能够被序列化的对象
如果pickle一个不支持序列化的对象时将会抛出PicklingError。
常见的方法
pickle.dump(obj,file,protocol=None,*,fix_imports=True)
将obj对象序列化到一个file文件中,该方法与Pickler(file,protocol).dump(obj)等价。
pickle.dumps(obj,protocol=None,*,fix_imports=True)
将obj对象序列化成bytes二进制数据。
pickle.load(file,*,fix_imports=True,encoding="ASCII",errors="strict")
从file文件中反序列化一个对象,该方法与Unpickler(file).load()等价。
pickle.loads(bytes_object,*,fix_imports=True,encoding="ASCII",errors="strict")
从二进制数据bytes_object反序列化对象。
序列化例子
importpickle
#定义了一个包含了可以被序列化对象的字典
data={
'a':[1,2.0,3,4+6j],
'b':("characterstring",b"bytestring"),
'c':{None,True,False}
}
withopen('data.pickle','wb')asf:
#序列化对象到一个data.pickle文件中
#指定了序列化格式的版本pickle.HIGHEST_PROTOCOL
pickle.dump(data,f,pickle.HIGHEST_PROTOCOL)
执行之后在文件夹中多一个data.pickle文件
serialization
├──data.pickle
├──pickles.py
└──unpickles.py
反序列化例子
importpickle
withopen('data.pickle','rb')asf:
#从data.pickle文件中反序列化对象
#pickle能够自动检测序列化文件的版本
#所以这里可以不用版本号
data=pickle.load(f)
print(data)
#执行后结果
#{'a':[1,2.0,3,(4+6j)],'b':('characterstring',b'bytestring'),'c':{False,True,None}}
0x02json
json是与语言无关,非常通用的数据交互格式。在Python它与marshal和pickle一样拥有相似的API。
常见的方法
json.dump(obj,fp,*,skipkeys=False,ensure_ascii=True,check_circular=True,allow_nan=True,cls=None,indent=None,separators=None,default=None,sort_keys=False,**kw)
序列化对象到fp文件中
json.dumps(obj,*,skipkeys=False,ensure_ascii=True,check_circular=True,allow_nan=True,cls=None,indent=None,separators=None,default=None,sort_keys=False,**kw)
将obj序列化成json对象
json.load(fp,*,cls=None,object_hook=None,parse_float=None,parse_int=None,parse_constant=None,object_pairs_hook=None,**kw)
从文件中反序列化成一个对象
json.loads(s,*,encoding=None,cls=None,object_hook=None,parse_float=None,parse_int=None,parse_constant=None,object_pairs_hook=None,**kw)
从json格式文档中反序列化成一个对象
json与Python对象的转化对照表
| JSON | Python |
|---|---|
| object | dict |
| list,tuple | array |
| str | string |
| int,float,int-&float-derivedEnums | number |
| True | true |
| False | false |
| None | null |
对于基本类型、序列、以及包含基本类型的集合类型json都可以很好的完成序列化工作。
序列化例子
>>>importjson
>>>json.dumps(['foo',{'bar':('baz',None,1.0,2)}])
'["foo",{"bar":["baz",null,1.0,2]}]'
>>>print(json.dumps("\"foo\bar"))
"\"foo\bar"
>>>print(json.dumps('\u1234'))
"\u1234"
>>>print(json.dumps('\\'))
"\\"
>>>print(json.dumps({"c":0,"b":0,"a":0},sort_keys=True))
{"a":0,"b":0,"c":0}
>>>fromioimportStringIO
>>>io=StringIO()
>>>json.dump(['streamingAPI'],io)
>>>io.getvalue()
'["streamingAPI"]'
反序列化例子
>>>importjson
>>>json.loads('["foo",{"bar":["baz",null,1.0,2]}]')
['foo',{'bar':['baz',None,1.0,2]}]
>>>json.loads('"\\"foo\\bar"')
'"foo\x08ar'
>>>fromioimportStringIO
>>>io=StringIO('["streamingAPI"]')
>>>json.load(io)
['streamingAPI']
对于object的情况就复杂一些了
例如定义了复数complex对象的json文档
complex_data.json
{
"__complex__":true,
"real":42,
"imaginary":36
}
要把这个json文档反序列化成Python对象,就需要定义转化的方法
#coding=utf-8
importjson
#定义转化函数,将json中的内容转化成complex对象
defdecode_complex(dct):
if"__complex__"indct:
returncomplex(dct["real"],dct["imaginary"])
else:
returndct
if__name__=='__main__':
withopen("complex_data.json")ascomplex_data:
#object_hook指定转化的函数
z=json.load(complex_data,object_hook=decode_complex)
print(type(z))
print(z)
#执行结果
#
#(42+36j)
如果不指定object_hook,那么默认将json文档中的object转成dict
#coding=utf-8
importjson
if__name__=='__main__':
withopen("complex_data.json")ascomplex_data:
#这里不指定object_hook
z2=json.loads(complex_data.read())
print(type(z2))
print(z2)
#执行结果
#
#{'__complex__':True,'real':42,'imaginary':36}
可以看到json文档中的object转成了dict对象。
一般情况下这样使用似乎也没什么问题,但如果对类型要求很高的场景就需要明确定义转化的方法了。
除了object_hook参数还可以使用json.JSONEncoder
importjson classComplexEncoder(json.JSONEncoder): defdefault(self,obj): ifisinstance(obj,complex): #如果complex对象这里转成数组的形式 return[obj.real,obj.imag] #默认处理 returnjson.JSONEncoder.default(self,obj) if__name__=='__main__': c=json.dumps(2+1j,cls=ComplexEncoder) print(type(c)) print(c) #执行结果 ##[2.0,1.0]
因为json模块并不是对所有类型都能够自动完成序列化的,对于不支持的类型,会直接抛出TypeError。
>>>importdatetime
>>>d=datetime.datetime.now()
>>>dct={'birthday':d,'uid':124,'name':'jack'}
>>>dct
{'birthday':datetime.datetime(2019,6,14,11,16,17,434361),'uid':124,'name':'jack'}
>>>json.dumps(dct)
Traceback(mostrecentcalllast):
File"",line1,in
json.dumps(dct)
File"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py",line231,indumps
return_default_encoder.encode(obj)
File"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py",line199,inencode
chunks=self.iterencode(o,_one_shot=True)
File"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py",line257,initerencode
return_iterencode(o,0)
File"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py",line179,indefault
raiseTypeError(f'Objectoftype{o.__class__.__name__}'
TypeError:ObjectoftypedatetimeisnotJSONserializable
对于不支持序列化的类型例如datetime以及自定义类型,就需要使用JSONEncoder来定义转化的逻辑。
importjson
importdatetime
#定义日期类型的JSONEncoder
classDatetimeEncoder(json.JSONEncoder):
defdefault(self,obj):
ifisinstance(obj,datetime.datetime):
returnobj.strftime('%Y-%m-%d%H:%M:%S')
elifisinstance(obj,datetime.date):
returnobj.strftime('%Y-%m-%d')
else:
returnjson.JSONEncoder.default(self,obj)
if__name__=='__main__':
d=datetime.date.today()
dct={"birthday":d,"name":"jack"}
data=json.dumps(dct,cls=DatetimeEncoder)
print(data)
#执行结果
#{"birthday":"2019-06-14","name":"jack"}
现在我们希望发序列化时,能够将json文档中的日期格式转化成datetime.date对象,这时就需要使用到json.JSONDecoder了。
#coding=utf-8
importjson
importdatetime
#定义Decoder解析json
classDatetimeDecoder(json.JSONDecoder):
#构造方法
def__init__(self):
super().__init__(object_hook=self.dict2obj)
defdict2obj(self,d):
ifisinstance(d,dict):
forkind:
ifisinstance(d[k],str):
#对日期格式进行解析,生成一个date对象
dat=d[k].split("-")
iflen(dat)==3:
date=datetime.date(int(dat[0]),int(dat[1]),int(dat[2]))
d[k]=date
returnd
if__name__=='__main__':
d=datetime.date.today()
dct={"birthday":d,"name":"jack"}
data=json.dumps(dct,cls=DatetimeEncoder)
#print(data)
obj=json.loads(data,cls=DatetimeDecoder)
print(type(obj))
print(obj)
#执行结果
#{"birthday":"2019-06-14","name":"jack"}
#
#{'birthday':datetime.date(2019,6,14),'name':'jack'}
0x03总结一下
Python常见的序列化工具有marshal、pickle和json。marshal主要用于Python的.pyc文件,并与Python版本相关。它不能序列化用户定义的类。
pickle是Python对象的序列化工具则比marshal更通用些,它可以兼容Python的不同版本。json是一种语言无关的数据结构,广泛用于各种网络应用尤其在RESTAPI的服务中的数据交互。
0x04学习资料
- docs.python.org/3/library/m…
- docs.python.org/3/library/p…
- docs.python.org/3/library/j…
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。