序列化方法

在程序運行的過程中,所有的變量都是在內(nèi)存中碳柱,比如构哺,定義一個dict:

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

可以隨時修改變量革答,比如把name改成'Bill'战坤,但是一旦程序結(jié)束,變量所占用的內(nèi)存就被操作系統(tǒng)全部回收残拐。如果沒有把修改后的'Bill'存儲到磁盤上途茫,下次重新運行程序,變量又被初始化為'Bob'溪食。

我們把變量從內(nèi)存中變成可存儲或傳輸?shù)倪^程稱之為序列化囊卜,在Python中叫pickling,在其他語言中也被稱之為serialization错沃,marshalling栅组,flattening等等,都是一個意思枢析。

序列化之后玉掸,就可以把序列化后的內(nèi)容寫入磁盤,或者通過網(wǎng)絡傳輸?shù)絼e的機器上醒叁。

反過來司浪,把變量內(nèi)容從序列化的對象重新讀到內(nèi)存里稱之為反序列化,即unpickling把沼。

Python提供兩個模塊來實現(xiàn)序列化:cPicklepickle啊易。這兩個模塊功能是一樣的,區(qū)別在于cPickle是C語言寫的智政,速度快认罩,pickle是純Python寫的,速度慢续捂,跟cStringIOStringIO一個道理垦垂。用的時候,先嘗試導入cPickle牙瓢,如果失敗劫拗,再導入pickle

try:
    import cPickle as pickle
except ImportError:
    import pickle

首先,我們嘗試把一個對象序列化并寫入文件:

>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
"(dp0\nS'age'\np1\nI20\nsS'score'\np2\nI88\nsS'name'\np3\nS'Bob'\np4\ns."

pickle.dumps()方法把任意對象序列化成一個str矾克,然后页慷,就可以把這個str寫入文件⌒哺剑或者用另一個方法pickle.dump()直接把對象序列化后寫入一個file-like Object:

>>> f = open('dump.txt', 'wb')
>>> pickle.dump(d, f)
>>> f.close()

看看寫入的dump.txt文件酒繁,一堆亂七八糟的內(nèi)容,這些都是Python保存的對象內(nèi)部信息控妻。

當我們要把對象從磁盤讀到內(nèi)存時州袒,可以先把內(nèi)容讀到一個str,然后用pickle.loads()方法反序列化出對象弓候,也可以直接用pickle.load()方法從一個file-like Object中直接反序列化出對象郎哭。我們打開另一個Python命令行來反序列化剛才保存的對象:

>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}

變量的內(nèi)容又回來了他匪!

當然,這個變量和原來的變量是完全不相干的對象夸研,它們只是內(nèi)容相同而已邦蜜。

Pickle的問題和所有其他編程語言特有的序列化問題一樣,就是它只能用于Python亥至,并且可能不同版本的Python彼此都不兼容悼沈,因此,只能用Pickle保存那些不重要的數(shù)據(jù)抬闯,不能成功地反序列化也沒關系井辆。

JSON

如果我們要在不同的編程語言之間傳遞對象,就必須把對象序列化為標準格式溶握,比如XML,但更好的方法是序列化為JSON蒸播,因為JSON表示出來就是一個字符串睡榆,可以被所有語言讀取,也可以方便地存儲到磁盤或者通過網(wǎng)絡傳輸袍榆。JSON不僅是標準格式胀屿,并且比XML更快,而且可以直接在Web頁面中讀取包雀,非常方便宿崭。

JSON表示的對象就是標準的JavaScript語言的對象,JSON和Python內(nèi)置的數(shù)據(jù)類型對應如下:

JSON類型 Python類型
{} dict
[] list
"string" 'str'或u'unicode'
1234.56 int或float
true/false True/False
null None

Python內(nèi)置的json模塊提供了非常完善的Python對象到JSON格式的轉(zhuǎn)換才写。我們先看看如何把Python對象變成一個JSON:

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

dumps()方法返回一個str葡兑,內(nèi)容就是標準的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)
{u'age': 20, u'score': 88, u'name': u'Bob'}

有一點需要注意,就是反序列化得到的所有字符串對象默認都是unicode而不是str沾凄。由于JSON標準規(guī)定JSON編碼是UTF-8梗醇,所以我們總是能正確地在Python的strunicode與JSON的字符串之間轉(zhuǎn)換。

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: <__main__.Student object at 0x10aabef50> is not JSON serializable

錯誤的原因是Student對象不是一個可序列化為JSON的對象。

如果連class的實例對象都無法序列化為JSON虫溜,這肯定不合理雹姊!

別急,我們仔細看看dumps()方法的參數(shù)列表衡楞,可以發(fā)現(xiàn)吱雏,除了第一個必須的obj參數(shù)外,dumps()方法還提供了一大堆的可選參數(shù):

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

這些可選參數(shù)就是讓我們來定制JSON序列化瘾境。前面的代碼之所以無法把Student類實例序列化為JSON歧杏,是因為默認情況下,dumps()方法不知道如何將Student實例變?yōu)橐粋€JSON的{}對象迷守。

可選參數(shù)default就是把任意一個對象變成一個可序列為JSON的對象犬绒,我們只需要為Student專門寫一個轉(zhuǎn)換函數(shù),再把函數(shù)傳進去即可:

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

print(json.dumps(s, default=student2dict))

這樣兑凿,Student實例首先被student2dict()函數(shù)轉(zhuǎn)換成dict凯力,然后再被順利序列化為JSON。

不過礼华,下次如果遇到一個Teacher類的實例咐鹤,照樣無法序列化為JSON。我們可以偷個懶圣絮,把任意class的實例變?yōu)?code>dict:

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

因為通常class的實例都有一個__dict__屬性祈惶,它就是一個dict,用來存儲實例變量扮匠。也有少數(shù)例外捧请,比如定義了__slots__的class。

同樣的道理餐禁,如果我們要把JSON反序列化為一個Student對象實例血久,loads()方法首先轉(zhuǎn)換出一個dict對象,然后帮非,我們傳入的object_hook函數(shù)負責把dict轉(zhuǎn)換為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))

運行結(jié)果如下:

<__main__.Student object at 0x10cd3c190>

打印出的是反序列化的Student實例對象氧吐。

小結(jié)

Python語言特定的序列化模塊是pickle,但如果要把序列化搞得更通用末盔、更符合Web標準筑舅,就可以使用json模塊。

json模塊的dumps()loads()函數(shù)是定義得非常好的接口的典范陨舱。當我們使用時翠拣,只需要傳入一個必須的參數(shù)。但是游盲,當默認的序列化或反序列機制不滿足我們的要求時误墓,我們又可以傳入更多的參數(shù)來定制序列化或反序列化的規(guī)則蛮粮,既做到了接口簡單易用,又做到了充分的擴展性和靈活性谜慌。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末然想,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子欣范,更是在濱河造成了極大的恐慌变泄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恼琼,死亡現(xiàn)場離奇詭異妨蛹,居然都是意外死亡,警方通過查閱死者的電腦和手機晴竞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門蛙卤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人噩死,你說我怎么就攤上這事表窘。” “怎么了甜滨?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瘤袖。 經(jīng)常有香客問我衣摩,道長,這世上最難降的妖魔是什么捂敌? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任艾扮,我火速辦了婚禮,結(jié)果婚禮上占婉,老公的妹妹穿的比我還像新娘泡嘴。我一直安慰自己,他們只是感情好逆济,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布酌予。 她就那樣靜靜地躺著,像睡著了一般奖慌。 火紅的嫁衣襯著肌膚如雪抛虫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天简僧,我揣著相機與錄音建椰,去河邊找鬼。 笑死岛马,一個胖子當著我的面吹牛棉姐,可吹牛的內(nèi)容都是我干的屠列。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼伞矩,長吁一口氣:“原來是場噩夢啊……” “哼笛洛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扭吁,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤撞蜂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后侥袜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝌诡,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年枫吧,在試婚紗的時候發(fā)現(xiàn)自己被綠了浦旱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡九杂,死狀恐怖颁湖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情例隆,我是刑警寧澤甥捺,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站镀层,受9級特大地震影響镰禾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唱逢,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一吴侦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坞古,春花似錦备韧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至听怕,卻和暖如春捧挺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尿瞭。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工闽烙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓黑竞,卻偏偏與公主長得像捕发,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子很魂,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 如果我們要操作文件扎酷、目錄,可以在命令行下面輸入操作系統(tǒng)提供的各種命令來完成遏匆。比如dir法挨、cp等命令。 如果要在Py...
    全宇宙最帥De男人閱讀 517評論 0 0
  • http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958...
    喵在野閱讀 318評論 0 0
  • 在程序運行的過程中幅聘,所有的變量都是在內(nèi)存中凡纳,比如,定義一個dict: 可以隨時修改變量帝蒿,比如把name改成'Bil...
    豬豬9527閱讀 386評論 1 0
  • 在程序運行的過程中荐糜,所有的變量都是在內(nèi)存中,比如葛超,定義一個dict: 可以隨時修改變量暴氏,比如把name改成Bill...
    劉光軍_MVP閱讀 481評論 0 1
  • 概述 Notification大家肯定不會陌生,并且使用方式也十分簡單明了绣张,使用場景也相對單一答渔,所以除了基礎用法外...
    Hly_Coder閱讀 1,994評論 0 8