1101|序列化

http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143192607210600a668b5112e4a979dd20e4661cc9c97000

解釋lambda:

lambda 參數(shù):參數(shù)運(yùn)算規(guī)則

f = lambda x,y,z:xyz
f(1,2,3)

結(jié)果:
6


在程序運(yùn)行的過(guò)程中,所有的變量都是在內(nèi)存中,比如,定義一個(gè)dict:

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

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

我們把變量從內(nèi)存中變成可存儲(chǔ)或傳輸?shù)倪^(guò)程稱之為序列化,在Python中叫pickling,在其他語(yǔ)言中也被稱之為serialization,marshalling,flattening等等,都是一個(gè)意思。

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

反過(guò)來(lái),把變量?jī)?nèi)容從序列化的對(duì)象重新讀到內(nèi)存里稱之為反序列化,即unpickling。

Python提供了pickle模塊來(lái)實(shí)現(xiàn)序列化。

首先,我們嘗試把一個(gè)對(duì)象序列化并寫入文件:

>>> import pickle
>>> d = dict(name='Bob', age=20, score=88)
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'

pickle.dumps()方法把任意對(duì)象序列化成一個(gè)bytes,然后,就可以把這個(gè)bytes寫入文件?;蛘哂昧硪粋€(gè)方法pickle.dump()直接把對(duì)象序列化后寫入一個(gè)file-like Object:

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

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

當(dāng)我們要把對(duì)象從磁盤讀到內(nèi)存時(shí),可以先把內(nèi)容讀到一個(gè)bytes,然后用pickle.loads()方法反序列化出對(duì)象,也可以直接用pickle.load()方法從一個(gè)file-like Object中直接反序列化出對(duì)象。我們打開另一個(gè)Python命令行來(lái)反序列化剛才保存的對(duì)象:

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

變量的內(nèi)容又回來(lái)了!

當(dāng)然,這個(gè)變量和原來(lái)的變量是完全不相干的對(duì)象,它們只是內(nèi)容相同而已。

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

JSON

如果我們要在不同的編程語(yǔ)言之間傳遞對(duì)象,就必須把對(duì)象序列化為標(biāo)準(zhǔn)格式,比如XML,但更好的方法是序列化為JSON,因?yàn)镴SON表示出來(lái)就是一個(gè)字符串,可以被所有語(yǔ)言讀取,也可以方便地存儲(chǔ)到磁盤或者通過(guò)網(wǎng)絡(luò)傳輸。JSON不僅是標(biāo)準(zhǔn)格式,并且比XML更快,而且可以直接在Web頁(yè)面中讀取,非常方便。

JSON表示的對(duì)象就是標(biāo)準(zhǔn)的JavaScript語(yǔ)言的對(duì)象,JSON和Python內(nèi)置的數(shù)據(jù)類型對(duì)應(yīng)如下:

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

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

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

dumps()方法返回一個(gè)str,內(nèi)容就是標(biāo)準(zhǔn)的JSON。類似的,dump()方法可以直接把JSON寫入一個(gè)file-like Object。

要把JSON反序列化為Python對(duì)象,用loads()或者對(duì)應(yīng)的load()方法,前者把JSON的字符串反序列化,后者從file-like Object中讀取字符串并反序列化:

>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> json.loads(json_str)
{'age': 20, 'score': 88, 'name': 'Bob'}

由于JSON標(biāo)準(zhǔn)規(guī)定JSON編碼是UTF-8,所以我們總是能正確地在Python的str與JSON的字符串之間轉(zhuǎn)換。

JSON進(jìn)階

Python的dict對(duì)象可以直接序列化為JSON的{},不過(guò),很多時(shí)候,我們更喜歡用class表示對(duì)象,比如定義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))

運(yùn)行代碼,毫不留情地得到一個(gè)TypeError:

Traceback (most recent call last):
  ...
TypeError: <__main__.Student object at 0x10603cc50> is not JSON serializable

錯(cuò)誤的原因是Student對(duì)象不是一個(gè)可序列化為JSON的對(duì)象。

如果連class的實(shí)例對(duì)象都無(wú)法序列化為JSON,這肯定不合理!

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

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

這些可選參數(shù)就是讓我們來(lái)定制JSON序列化。前面的代碼之所以無(wú)法把Student類實(shí)例序列化為JSON,是因?yàn)槟J(rèn)情況下,dumps()方法不知道如何將Student實(shí)例變?yōu)橐粋€(gè)JSON的{}對(duì)象。

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

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

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

>>> print(json.dumps(s, default=student2dict))
{"age": 20, "name": "Bob", "score": 88}

不過(guò),下次如果遇到一個(gè)Teacher類的實(shí)例,照樣無(wú)法序列化為JSON。我們可以偷個(gè)懶,把任意class的實(shí)例變?yōu)閐ict:

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

因?yàn)橥ǔlass的實(shí)例都有一個(gè)dict屬性,它就是一個(gè)dict,用來(lái)存儲(chǔ)實(shí)例變量。也有少數(shù)例外,比如定義了slots的class。

同樣的道理,如果我們要把JSON反序列化為一個(gè)Student對(duì)象實(shí)例,loads()方法首先轉(zhuǎn)換出一個(gè)dict對(duì)象,然后,我們傳入的object_hook函數(shù)負(fù)責(zé)把dict轉(zhuǎn)換為Student實(shí)例:

def dict2student(d):
    return Student(d['name'], d['age'], d['score'])

運(yùn)行結(jié)果如下:

>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>

打印出的是反序列化的Student實(shí)例對(duì)象。

小結(jié)

Python語(yǔ)言特定的序列化模塊是pickle,但如果要把序列化搞得更通用、更符合Web標(biāo)準(zhǔn),就可以使用json模塊。

json模塊的dumps()和loads()函數(shù)是定義得非常好的接口的典范。當(dāng)我們使用時(shí),只需要傳入一個(gè)必須的參數(shù)。但是,當(dāng)默認(rèn)的序列化或反序列機(jī)制不滿足我們的要求時(shí),我們又可以傳入更多的參數(shù)來(lái)定制序列化或反序列化的規(guī)則,既做到了接口簡(jiǎn)單易用,又做到了充分的擴(kuò)展性和靈活性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • IO編程概念 IO在計(jì)算機(jī)中指Input/Output,也就是輸入和輸出。由于程序和運(yùn)行時(shí)數(shù)據(jù)是在內(nèi)存中駐留,由C...
    時(shí)間之友閱讀 774評(píng)論 0 0
  • 我們把變量從內(nèi)存中變成可存儲(chǔ)或傳輸?shù)倪^(guò)程稱之為序列化,在Python中叫pickling,序列化之后,就可以把序列...
    syp_xp閱讀 227評(píng)論 0 0
  • 基礎(chǔ)1.r''表示''內(nèi)部的字符串默認(rèn)不轉(zhuǎn)義2.'''...'''表示多行內(nèi)容3. 布爾值:True、False(...
    neo已經(jīng)被使用閱讀 1,879評(píng)論 0 5
  • 這是社群運(yùn)營(yíng)的核心關(guān)鍵所在,但大道相通,我感覺這三點(diǎn)準(zhǔn)則應(yīng)用在與人際關(guān)系相關(guān)的領(lǐng)域。 同道,為底線。道不同不相為謀...
    Nickoh閱讀 899評(píng)論 0 0
  • 本地服務(wù)器<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><n...
    代碼詩(shī)仁閱讀 379評(píng)論 0 0

友情鏈接更多精彩內(nèi)容