python IO編程之序列化

python學(xué)習(xí)筆記,特做記錄,分享給大家,希望對(duì)大家有所幫助。

序列化

在程序運(yùn)行的過程中,所有的變量都是在內(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ù)倪^程稱之為序列化,在Python中叫pickling,在其他語言中也被稱之為serialization,marshalling,flattening等等,都是一個(gè)意思。

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

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

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

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

import pickle
d = dict(name='Bob', age=20, score=88)
pickle.dumps(d)

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

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.'

Process finished with exit code 0

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命令行來反序列化剛才保存的對(duì)象:

f = open('dump.txt', 'rb')
d = pickle.load(f)
f.close()
print d

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

{'age': 20, 'score': 88, 'name': 'Bob'}

Process finished with exit code 1

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

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

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

JSON

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

JSON表示的對(duì)象就是標(biāo)準(zhǔn)的JavaScript語言的對(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)
print json.dumps(d)

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

{"age": 20, "score": 88, "name": "Bob"}

Process finished with exit code 0

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"}'
print json.loads(json_str)

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

{u'age': 20, u'score': 88, u'name': u'Bob'}

Process finished with exit code 0

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

JSON進(jìn)階

Python的dict對(duì)象可以直接序列化為JSON的{},不過,很多時(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:

TypeError: <__main__.Student object at 0x10603cc50> is not JSON serializable

Process finished with exit code 1

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

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

這些可選參數(shù)就是讓我們來定制JSON序列化。前面的代碼之所以無法把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)

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

{"age": 20, "score": 88, "name": "Bob"}

Process finished with exit code 0

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

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

因?yàn)橥ǔlass的實(shí)例都有一個(gè)dict屬性,它就是一個(gè)dict,用來存儲(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é)果如下:

<__main__.Student object at 0x10cd3c190>

Process finished with exit code 1

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

?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • pickle模塊,json模塊 (1)把變量從內(nèi)存中變成可存儲(chǔ)或傳輸?shù)倪^程,稱之為序列化。Python中叫pick...
    2023開始學(xué)閱讀 1,011評(píng)論 0 0
  • IO編程概念 IO在計(jì)算機(jī)中指Input/Output,也就是輸入和輸出。由于程序和運(yùn)行時(shí)數(shù)據(jù)是在內(nèi)存中駐留,由C...
    時(shí)間之友閱讀 782評(píng)論 0 0
  • 一·input和raw_inputinput()輸入嚴(yán)格按照Python的語法,是字符就加 ' ' ,數(shù)字就是數(shù)字...
    只喝白開水a(chǎn)閱讀 1,044評(píng)論 0 0
  • 高階函數(shù):將函數(shù)作為參數(shù) sortted()它還可以接收一個(gè)key函數(shù)來實(shí)現(xiàn)自定義的排序,reversec參數(shù)可反...
    royal_47a2閱讀 840評(píng)論 0 0
  • 不忍看,鵲橋上,牛郎織女霎兒歡。 銀漢迢迢恨不休。 他叫一聲妹,她喚一聲哥,淚滂沱。
    花海弄舟閱讀 264評(píng)論 0 3

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