0x00 marshal
marshal使用的是與Python語言相關(guān)但與機器無關(guān)的二進制來讀寫Python對象的。這種二進制的格式也跟Python語言的版本相關(guān),marshal序列化的格式對不同的版本的Python是不兼容的。
marshal一般用于Python內(nèi)部對象的序列化。
一般地包括:
- 基本類型 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版本不兼容的原因。開發(fā)者如果要使用序列化/反序列化,那么應(yīng)該使用pickle模塊。
常見的方法
marshal.dump(value, file[, version])
序列化一個對象到文件中
marshal.dumps(value[, version])
序列化一個對象并返回一個bytes對象
marshal.load(file)
從文件中反序列化一個對象
marshal.loads(bytes)
從bytes二進制數(shù)據(jù)中反序列化一個對象
0x01 pickle
pickle模塊也能夠以二進制的方式對Python對象進行讀寫。相比marshal提供基本的序列化能力,pickle的序列化應(yīng)用更加廣泛。
pickle序列化后的數(shù)據(jù)也是與Python語言相關(guān)的,即其它語言例如Java無法讀取由Python通過pickle序列化的二進制數(shù)據(jù)。如果要使用與語言無法的序列化那么我們應(yīng)該使用json。下文將會說明。
能被pickle序列化的數(shù)據(jù)類型有:
- None, True, and False
- integers, floating point numbers, complex numbers
- strings, bytes, bytearrays
- tuples, lists, sets, and dictionaries 以及包含可以被pickle序列化對象
- 在模塊頂層定義的函數(shù)對象 (使用 def定義的, 而不是 lambda表達式)
- 在模塊頂層定義內(nèi)置函數(shù)
- 在模式頂層定義的類
- 一個類的__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二進制數(shù)據(jù)。
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")
從二進制數(shù)據(jù)bytes_object反序列化對象。
序列化例子
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)
執(zhí)行之后在文件夾中多一個data.pickle文件
serialization
├── data.pickle
├── pickles.py
└── unpickles.py
反序列化例子
import pickle
with open('data.pickle', 'rb') as f:
# 從data.pickle文件中反序列化對象
# pickle能夠自動檢測序列化文件的版本
# 所以這里可以不用版本號
data = pickle.load(f)
print(data)
# 執(zhí)行后結(jié)果
# {'a': [1, 2.0, 3, (4+6j)], 'b': ('character string', b'byte string'), 'c': {False, True, None}}
0x02 json
json是與語言無關(guān),非常通用的數(shù)據(jù)交互格式。在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對象的轉(zhuǎn)化對照表
| JSON | Python |
|---|---|
| object | dict |
| list,tuple | array |
| str | string |
| int, float, int- & float-derived Enums | number |
| True | true |
| False | false |
| None | null |
對于基本類型、序列、以及包含基本類型的集合類型json都可以很好的完成序列化工作。
序列化例子
>>> 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"]'
反序列化例子
>>> 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的情況就復(fù)雜一些了
例如定義了復(fù)數(shù)complex對象的json文檔
complex_data.json
{
"__complex__": true,
"real": 42,
"imaginary": 36
}
要把這個json文檔反序列化成Python對象,就需要定義轉(zhuǎn)化的方法
# coding=utf-8
import json
# 定義轉(zhuǎn)化函數(shù),將json中的內(nèi)容轉(zhuǎn)化成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指定轉(zhuǎn)化的函數(shù)
z = json.load(complex_data, object_hook=decode_complex)
print(type(z))
print(z)
# 執(zhí)行結(jié)果
#
# (42+36j)
如果不指定object_hook,那么默認將json文檔中的object轉(zhuǎn)成dict
# 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)
# 執(zhí)行結(jié)果
#
# {'__complex__': True, 'real': 42, 'imaginary': 36}
可以看到j(luò)son文檔中的object轉(zhuǎn)成了dict對象。
一般情況下這樣使用似乎也沒什么問題,但如果對類型要求很高的場景就需要明確定義轉(zhuǎn)化的方法了。
除了object_hook參數(shù)還可以使用json.JSONEncoder
import json
class ComplexEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, complex):
# 如果complex對象這里轉(zhuǎn)成數(shù)組的形式
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)
# 執(zhí)行結(jié)果
#
# [2.0, 1.0]
因為json模塊并不是對所有類型都能夠自動完成序列化的,對于不支持的類型,會直接拋出TypeError。
>>> 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 "
", line 1, in
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來定義轉(zhuǎn)化的邏輯。
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)
# 執(zhí)行結(jié)果
# {"birthday": "2019-06-14", "name": "jack"}
現(xiàn)在我們希望發(fā)序列化時,能夠?qū)son文檔中的日期格式轉(zhuǎn)化成datetime.date對象,這時就需要使用到j(luò)son.JSONDecoder了。
# coding=utf-8
import json
import datetime
# 定義Decoder解析json
class DatetimeDecoder(json.JSONDecoder):
# 構(gòu)造方法
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)
# 執(zhí)行結(jié)果
# {"birthday": "2019-06-14", "name": "jack"}
#
# {'birthday': datetime.date(2019, 6, 14), 'name': 'jack'}
0x03 總結(jié)一下
Python常見的序列化工具有marshal、pickle和json。marshal主要用于Python的.pyc文件,并與Python版本相關(guān)。它不能序列化用戶定義的類。
pickle是Python對象的序列化工具則比marshal更通用些,它可以兼容Python的不同版本。json是一種語言無關(guān)的數(shù)據(jù)結(jié)構(gòu),廣泛用于各種網(wǎng)絡(luò)應(yīng)用尤其在REST API的服務(wù)中的數(shù)據(jù)交互。
0x04 學(xué)習資料
- docs.python.org/3/library/m…
- docs.python.org/3/library/p…
- docs.python.org/3/library/j…
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,謝謝大家對腳本之家的支持。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

