Python的數(shù)據(jù)序列化「Json & Pickle」
在介紹Python的數(shù)據(jù)序列化模塊「Json & Pickle」之前,我們先來(lái)看看為什么需要數(shù)據(jù)序列化,什么是數(shù)據(jù)序列化。
為什么需要數(shù)據(jù)序列化,我認(rèn)為有如下兩種原因:
一個(gè)原因是將對(duì)象(一切皆對(duì)象)的狀態(tài)保持在存儲(chǔ)媒介(硬盤(pán)、網(wǎng)盤(pán)......)中,以便可以在以后重新創(chuàng)建精確的副本,相當(dāng)于鏡像的概念,比如我們平時(shí)利用VMware虛擬機(jī)中的掛起功能,這個(gè)掛起功能就是利用數(shù)據(jù)的序列化,把虛擬機(jī)當(dāng)前的狀態(tài)序列化保存在本地磁盤(pán)的文件中,然后恢復(fù)的時(shí)候只需反序列化,把狀態(tài)恢復(fù)即可。
另一個(gè)原因是通過(guò)值將對(duì)象從一個(gè)應(yīng)用程序域發(fā)送到另一個(gè)應(yīng)用程序域中。例如,你利用Python監(jiān)控采集程序采集到的數(shù)據(jù)想傳送給Zabbix處理。當(dāng)兩個(gè)進(jìn)程在進(jìn)行遠(yuǎn)程通信時(shí),彼此可以發(fā)送各種類(lèi)型的數(shù)據(jù)。無(wú)論是何種類(lèi)型的數(shù)據(jù),都會(huì)以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。發(fā)送方需要把這個(gè)對(duì)象轉(zhuǎn)換為字節(jié)序列,才能在網(wǎng)絡(luò)上傳送;接收方則需要把字節(jié)序列再恢復(fù)為對(duì)象。
序列化和反序列化:
- 序列化: 將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過(guò)程。
- 反序列化:將在序列化過(guò)程中所生成的二進(jìn)制串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對(duì)象的過(guò)程。
序列化的目的就是為了跨進(jìn)程傳遞格式化數(shù)據(jù)和保存某個(gè)時(shí)刻的狀態(tài)。
什么是數(shù)據(jù)序列化:
數(shù)據(jù)序列化就是將對(duì)象或者數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化成特定的格式,使其可在網(wǎng)絡(luò)中傳輸,或者可存儲(chǔ)在內(nèi)存或者文件中。反序列化則是相反的操作,將對(duì)象從序列化數(shù)據(jù)中還原出來(lái)。而對(duì)象序列化后的數(shù)據(jù)格式可以是二進(jìn)制,可以是XML,也可以是JSON等任何格式。對(duì)象/數(shù)據(jù)序列化的重點(diǎn)在于數(shù)據(jù)的交換和傳輸,例如在遠(yuǎn)程調(diào)用技術(shù)(如EJB,XML-RPC, Web Service),或者在GUI控件開(kāi)發(fā)(JavaBean)等等。
清楚了數(shù)據(jù)格式化的必要和簡(jiǎn)單認(rèn)識(shí)了什么是數(shù)據(jù)格式化之后,我們就來(lái)看看Python中兩個(gè)數(shù)據(jù)格式化模塊的使用。
Json Module
Json:用于字符串和 python數(shù)據(jù)類(lèi)型間進(jìn)行轉(zhuǎn)換;
Json模塊提供了四個(gè)功能:dumps、dump、loads、load
1.dumps把數(shù)據(jù)類(lèi)型轉(zhuǎn)換成字符串
2.dump把數(shù)據(jù)類(lèi)型轉(zhuǎn)換成字符串并存儲(chǔ)在文件中
3.loads把字符串轉(zhuǎn)換成數(shù)據(jù)類(lèi)型
4.load把文件打開(kāi)從字符串轉(zhuǎn)換成數(shù)據(jù)類(lèi)型
實(shí)例如下:
- #!/usr/bin/env python3
- # _*_coding:utf-8_*_
- # Author: Lucky.chen
- import json
- info = {'1MinLoad': 5, 'MemUse': '5G', 'DiskUse': '80G'}
- print('dumps 操作之前數(shù)據(jù)類(lèi)型: %s' % type(info))
- JsonInfo = json.dumps(info)
- print(JsonInfo)
- # dumps 將數(shù)據(jù)通過(guò)特殊的形式轉(zhuǎn)換為所有程序語(yǔ)言都識(shí)別的字符串
- print('dumps 操作之后數(shù)據(jù)類(lèi)型: %s' % type(JsonInfo))
- # loads 將字符串通過(guò)特殊的形式轉(zhuǎn)為python是數(shù)據(jù)類(lèi)型 (將字符串轉(zhuǎn)為字典)
- NewInfo = json.loads(JsonInfo)
- print('loads 操作之后數(shù)據(jù)類(lèi)型為: %s' % type(NewInfo))
- print('分割線'.center(50, '-'))
- # dump 將數(shù)據(jù)通過(guò)特殊的形式轉(zhuǎn)換為所有語(yǔ)言都識(shí)別的字符串并寫(xiě)入文件
- with open('SystemInfo.txt', 'w') as f:
- json.dump(info, f)
- print('dump file end!!')
- # load 從文件讀取字符串并轉(zhuǎn)換為python的數(shù)據(jù)類(lèi)型
- with open('SystemInfo.txt', 'r') as f:
- LoadInfo = json.load(f)
- print('load file end, data type is %s' % type(LoadInfo), LoadInfo)
結(jié)果如下:
- dumps 操作之前數(shù)據(jù)類(lèi)型: <class 'dict'>
- {"MemUse": "5G", "DiskUse": "80G", "1MinLoad": 5}
- dumps 操作之后數(shù)據(jù)類(lèi)型: <class 'str'>
- loads 操作之后數(shù)據(jù)類(lèi)型為: <class 'dict'>
- -----------------------分割線------------------------
- dump file end!!
- load file end, data type is <class 'dict'> {'MemUse': '5G', '1MinLoad': 5, 'DiskUse'
一個(gè)錯(cuò)誤案例如下:
- #!/usr/bin/env python3
- # _*_coding:utf-8_*_
- # Author: Lucky.chen
- import json
- def test():
- print('Test Func')
- info = {'Name': 'crh', 'age': 18, 'Func': test}
- json.dumps(info)
結(jié)果:
- raise TypeError(repr(o) + " is not JSON serializable")
- ypeError: <function test at 0x108e7a0d0> is not JSON serializable
如上可知函數(shù)不能被json序列化。
Pickle Module
pickle,用于python特有的類(lèi)型 和 python的數(shù)據(jù)類(lèi)型間進(jìn)行轉(zhuǎn)換
Pickle模塊同樣提供了四個(gè)功能:dumps、dump、loads、load
1.dumps把數(shù)據(jù)類(lèi)型轉(zhuǎn)換成字符串
2.dump把數(shù)據(jù)類(lèi)型轉(zhuǎn)換成字符串并存儲(chǔ)在文件中
3.oads把字符串轉(zhuǎn)換成數(shù)據(jù)類(lèi)型
4.load把文件打開(kāi)從字符串轉(zhuǎn)換成數(shù)據(jù)類(lèi)型
Pickle可以序列化一些較復(fù)雜的數(shù)據(jù),和json的區(qū)別在于pickle序列化的時(shí)候,存放的是二進(jìn)制的文件,所以打開(kāi)一個(gè)文件的時(shí)候,我們要以二進(jìn)制的格式打開(kāi)。
實(shí)例如下:
- #!/usr/bin/env python3
- # _*_coding:utf-8_*_
- # Author: Lucky.chen
- import pickle
- def test(name):
- print('%s write Test Func' % name)
- info = {'Name': 'crh', 'age': 18, 'Func': test}
- print('dumps 之前數(shù)據(jù)的類(lèi)型為: %s' % type(info))
- # pickle.dumps 將數(shù)據(jù)通過(guò)特殊的形式轉(zhuǎn)換為只有python語(yǔ)言認(rèn)識(shí)bytes類(lèi)型(Python2.*中是字符串類(lèi)型)
- NewInfo = pickle.dumps(info)
- print('dumps result is %s, data type is %s' % (NewInfo, type(NewInfo)))
- # pickle.loads 將bytes通過(guò)特殊的形式轉(zhuǎn)為python是數(shù)據(jù)類(lèi)型
- LoadInfo = pickle.loads(NewInfo)
- print('loads result is %s, data type is %s' % (LoadInfo, type(LoadInfo)))
- LoadInfo['Func']('crh')
- print('分割線'.center(50, '-'))
- # pickle.dump 將數(shù)據(jù)通過(guò)特殊的形式轉(zhuǎn)換為只有python語(yǔ)言認(rèn)識(shí)的字符串,并寫(xiě)入文件
- with open('pickle.rb', 'wb') as f:
- pickle.dump(info, f)
- # pickle.load 從文件讀取只有python語(yǔ)言認(rèn)識(shí)的字符串并轉(zhuǎn)換為python的數(shù)據(jù)類(lèi)型
- with open('pickle.rb', 'rb') as f:
- Info = pickle.load(f)
- print(Info, 'type is %s' % type(Info))
結(jié)果如下:
- dumps 之前數(shù)據(jù)的類(lèi)型為: <class 'dict'>
- dumps result is b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x12X\x04\x00\x00\x00Nameq\x02X\x03\x00\x00\x00crhq\x03X\x04\x00\x00\x00Funcq\x04c__main__\ntest\nq\x05u.', data type is <class 'bytes'>
- loads result is {'age': 18, 'Name': 'crh', 'Func': <function test at 0x1032f10d0>}, data type is <class 'dict'>
- crh write Test Func
- -----------------------分割線------------------------
- {'age': 18, 'Name': 'crh', 'Func': <function test at 0x1032f10d0>} type is <class 'di
總結(jié)
很多情況下不同的程序之間傳送數(shù)據(jù)我們一般通過(guò)文件的方式,但是這個(gè)方法是最原始的,而dumps可以直接讓數(shù)據(jù)格式化傳送給對(duì)方,但是不是所有的程序都是python的,所以只利用pickle是不現(xiàn)實(shí)的,比如一個(gè)python的程序需要發(fā)送一段數(shù)據(jù)給一個(gè)java程序開(kāi)發(fā)的應(yīng)用,這時(shí)候很多內(nèi)存數(shù)據(jù)的交換,就得用json了。
并且josn能dump的結(jié)果更可讀,那么有人就問(wèn)了,那還用pickle做什么不直接用josn,是這樣的josn只能把常用的數(shù)據(jù)類(lèi)型序列化(列表、字典、列表、字符串、數(shù)字、),比如日期格式、類(lèi)對(duì)象!josn就不行了。
為什么他不能序列化上面的東西呢?因?yàn)閖osn是跨語(yǔ)言的!注定了它只能規(guī)范出一些通用的數(shù)據(jù)類(lèi)型的格式,統(tǒng)一標(biāo)準(zhǔn)。