0x00 marshal
marshal使用的是与python语言相关但与机器无关的二进制来读写python对象的。这种二进制的格式也跟python语言的版本相关,marshal序列化的格式对不同的版本的python是不兼容的。
marshal一般用于python内部对象的序列化。
一般地包括:
- 基本类型 booleans, integers,floating point numbers,complex numbers
- 序列集合类型 strings, bytes, bytearray, tuple, list, set, frozenset, dictionary
- code对象 code object
- 其它类型 none, ellipsis, stopiteration
marshal的主要作用是对python“编译”的.pyc文件读写的支持。这也是marshal对python版本不兼容的原因。开发者如果要使用序列化/反序列化,那么应该使用pickle模块。
常见的方法
1
|
marshal.dump(value, file [, version])
|
序列化一个对象到文件中
1
|
marshal.dumps(value[, version])
|
序列化一个对象并返回一个bytes对象
1
|
marshal.load( file )
|
从文件中反序列化一个对象
1
|
marshal.loads(bytes)
|
从bytes二进制数据中反序列化一个对象
0x01 pickle
pickle模块也能够以二进制的方式对python对象进行读写。相比marshal提供基本的序列化能力,pickle的序列化应用更加广泛。
pickle序列化后的数据也是与python语言相关的,即其它语言例如java无法读取由python通过pickle序列化的二进制数据。如果要使用与语言无法的序列化那么我们应该使用json。下文将会说明。
能被pickle序列化的数据类型有:
- none, true, and false
- integers, floating point numbers, complex numbers
- strings, bytes, bytearrays
- tuples, lists, sets, and dictionaries 以及包含可以被pickle序列化对象
- 在模块顶层定义的函数对象 (使用 def定义的, 而不是 lambda表达式)
- 在模块顶层定义内置函数
- 在模式顶层定义的类
- 一个类的__dict__包含了可序列化的对象或__getstate__()方法返回了能够被序列化的对象
如果pickle一个不支持序列化的对象时将会抛出picklingerror。
常见的方法
1
|
pickle.dump(obj, file , protocol = none, * , fix_imports = true)
|
将obj对象序列化到一个file文件中,该方法与pickler(file, protocol).dump(obj)等价。
1
|
pickle.dumps(obj, protocol = none, * , fix_imports = true)
|
将obj对象序列化成bytes二进制数据。
1
|
pickle.load( file , * , fix_imports = true, encoding = "ascii" , errors = "strict" )
|
从file文件中反序列化一个对象,该方法与unpickler(file).load()等价。
1
|
pickle.loads(bytes_object, * , fix_imports = true, encoding = "ascii" , errors = "strict" )
|
从二进制数据bytes_object反序列化对象。
序列化例子
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import pickle
# 定义了一个包含了可以被序列化对象的字典
data = {
'a' : [ 1 , 2.0 , 3 , 4 + 6j ],
'b' : ( "character string" , b "byte string" ),
'c' : {none, true, false}
}
with open ( 'data.pickle' , 'wb' ) as f:
# 序列化对象到一个data.pickle文件中
# 指定了序列化格式的版本pickle.highest_protocol
pickle.dump(data, f, pickle.highest_protocol)
|
执行之后在文件夹中多一个data.pickle文件
serialization
├── data.pickle
├── pickles.py
└── unpickles.py
反序列化例子
1
2
3
4
5
6
7
8
9
10
11
12
|
import pickle
with open ( 'data.pickle' , 'rb' ) as f:
# 从data.pickle文件中反序列化对象
# pickle能够自动检测序列化文件的版本
# 所以这里可以不用版本号
data = pickle.load(f)
print (data)
# 执行后结果
# {'a': [1, 2.0, 3, (4+6j)], 'b': ('character string', b'byte string'), 'c': {false, true, none}}
|
0x02 json
json是与语言无关,非常通用的数据交互格式。在python它与marshal和pickle一样拥有相似的api。
常见的方法
1
|
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文件中
1
|
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对象
1
|
json.load(fp, * , cls = none, object_hook = none, parse_float = none, parse_int = none, parse_constant = none, object_pairs_hook = none, * * kw)
|
从文件中反序列化成一个对象
1
|
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-derived enums | number |
true | true |
false | false |
none | null |
对于基本类型、序列、以及包含基本类型的集合类型json都可以很好的完成序列化工作。
序列化例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>>> import json
>>> 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 }
>>> from io import stringio
>>> io = stringio()
>>> json.dump([ 'streaming api' ], io)
>>> io.getvalue()
'["streaming api"]'
|
反序列化例子
1
2
3
4
5
6
7
8
9
|
>>> import json
>>> json.loads( '["foo", {"bar":["baz", null, 1.0, 2]}]' )
[ 'foo' , { 'bar' : [ 'baz' , none, 1.0 , 2 ]}]
>>> json.loads( '"\\"foo\\bar"' )
'"foo\x08ar'
>>> from io import stringio
>>> io = stringio( '["streaming api"]' )
>>> json.load(io)
[ 'streaming api' ]
|
对于object的情况就复杂一些了
例如定义了复数complex对象的json文档
complex_data.json
1
2
3
4
5
|
{
"__complex__" : true,
"real" : 42 ,
"imaginary" : 36
}
|
要把这个json文档反序列化成python对象,就需要定义转化的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# coding=utf-8
import json
# 定义转化函数,将json中的内容转化成complex对象
def decode_complex(dct):
if "__complex__" in dct:
return complex (dct[ "real" ], dct[ "imaginary" ])
else :
return dct
if __name__ = = '__main__' :
with open ( "complex_data.json" ) as complex_data:
# object_hook指定转化的函数
z = json.load(complex_data, object_hook = decode_complex)
print ( type (z))
print (z)
# 执行结果
# <class 'complex'>
# (42+36j)
|
如果不指定object_hook,那么默认将json文档中的object转成dict
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# coding=utf-8
import json
if __name__ = = '__main__' :
with open ( "complex_data.json" ) as complex_data:
# 这里不指定object_hook
z2 = json.loads(complex_data.read())
print ( type (z2))
print (z2)
# 执行结果
# <class 'dict'>
# {'__complex__': true, 'real': 42, 'imaginary': 36}
|
可以看到json文档中的object转成了dict对象。
一般情况下这样使用似乎也没什么问题,但如果对类型要求很高的场景就需要明确定义转化的方法了。
除了object_hook参数还可以使用json.jsonencoder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import json
class complexencoder(json.jsonencoder):
def default( self , obj):
if isinstance (obj, complex ):
# 如果complex对象这里转成数组的形式
return [obj.real, obj.imag]
# 默认处理
return json.jsonencoder.default( self , obj)
if __name__ = = '__main__' :
c = json.dumps( 2 + 1j , cls = complexencoder)
print ( type (c))
print (c)
# 执行结果
# <class 'str'>
# [2.0, 1.0]
|
因为json模块并不是对所有类型都能够自动完成序列化的,对于不支持的类型,会直接抛出typeerror。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
>>> import datetime
>>> 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 (most recent call last):
file "<pyshell#19>" , line 1 , in <module>
json.dumps(dct)
file "/library/frameworks/python.framework/versions/3.7/lib/python3.7/json/__init__.py" , line 231 , in dumps
return _default_encoder.encode(obj)
file "/library/frameworks/python.framework/versions/3.7/lib/python3.7/json/encoder.py" , line 199 , in encode
chunks = self .iterencode(o, _one_shot = true)
file "/library/frameworks/python.framework/versions/3.7/lib/python3.7/json/encoder.py" , line 257 , in iterencode
return _iterencode(o, 0 )
file "/library/frameworks/python.framework/versions/3.7/lib/python3.7/json/encoder.py" , line 179 , in default
raise typeerror(f 'object of type {o.__class__.__name__} '
typeerror: object of type datetime is not json serializable
|
对于不支持序列化的类型例如datetime以及自定义类型,就需要使用jsonencoder来定义转化的逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import json
import datetime
# 定义日期类型的jsonencoder
class datetimeencoder(json.jsonencoder):
def default( self , obj):
if isinstance (obj, datetime.datetime):
return obj.strftime( '%y-%m-%d %h:%m:%s' )
elif isinstance (obj, datetime.date):
return obj.strftime( '%y-%m-%d' )
else :
return json.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了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
# coding=utf-8
import json
import datetime
# 定义decoder解析json
class datetimedecoder(json.jsondecoder):
# 构造方法
def __init__( self ):
super ().__init__(object_hook = self .dict2obj)
def dict2obj( self , d):
if isinstance (d, dict ):
for k in d:
if isinstance (d[k], str ):
# 对日期格式进行解析,生成一个date对象
dat = d[k].split( "-" )
if len (dat) = = 3 :
date = datetime.date( int (dat[ 0 ]), int (dat[ 1 ]), int (dat[ 2 ]))
d[k] = date
return d
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"}
# <class 'dict'>
# {'birthday': datetime.date(2019, 6, 14), 'name': 'jack'}
|
0x03 总结一下
python常见的序列化工具有marshal、pickle和json。marshal主要用于python的.pyc文件,并与python版本相关。它不能序列化用户定义的类。
pickle是python对象的序列化工具则比marshal更通用些,它可以兼容python的不同版本。json是一种语言无关的数据结构,广泛用于各种网络应用尤其在rest api的服务中的数据交互。
0x04 学习资料
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对服务器之家的支持。
原文链接:https://juejin.im/post/5d0362755188255e780b6815