第五章
5.1存儲(chǔ)
1.文件
我們知道峻呕,py中的數(shù)據(jù)都保存在內(nèi)存中利职,當(dāng)電腦斷電時(shí),就好像患了失憶癥
內(nèi)存中的數(shù)據(jù)就會(huì)消失瘦癌,另一方面猪贪,如果py程序運(yùn)行結(jié)束
那么分配給這個(gè)程序的內(nèi)存空間也會(huì)清空,為了長(zhǎng)期持續(xù)地存儲(chǔ)
py必須把數(shù)據(jù)存儲(chǔ)在磁盤(pán)中讯私,這樣热押,即使斷電或程序結(jié)束
數(shù)據(jù)依然存在
磁盤(pán)以文件為單位來(lái)存儲(chǔ)數(shù)據(jù)西傀,對(duì)于計(jì)算機(jī)來(lái)說(shuō),數(shù)據(jù)的本質(zhì)就是
有序的二進(jìn)制數(shù)序列桶癣,如果以字節(jié)為單位拥褂,也就是每8位二進(jìn)制數(shù)序列為單位
那么這個(gè)數(shù)據(jù)序列就稱(chēng)為文本,這是因?yàn)椋?位的二進(jìn)制數(shù)序列正好
對(duì)應(yīng)ASCII編碼中的一個(gè)字符牙寞,而py能夠借助文本對(duì)象來(lái)讀寫(xiě)文件
在py中饺鹃,我們可以通過(guò)內(nèi)置函數(shù)open來(lái)創(chuàng)建文件對(duì)象
在調(diào)用open時(shí),需要說(shuō)明文件名碎税,以及打開(kāi)文件的方式:
f = open(文件名尤慰,方式)
文件名是文件存在于磁盤(pán)的名字,打開(kāi)文件的常用方式有:
"r" #讀取已經(jīng)存在的文件
"w" #新建文件雷蹂,并寫(xiě)入
"a" #如果文件存在伟端,那么寫(xiě)入到文件的結(jié)尾,如果文件不存在匪煌,則新建文件并寫(xiě)入
例如:
f = open("test.txt","r")
就是用只讀的方式责蝠,打開(kāi)了一個(gè)名為test.txt的文件
通過(guò)返回上面的對(duì)象,我們可以讀取文件:
content = f.read(10) #讀取10個(gè)字節(jié)的數(shù)據(jù)
content = f.readline() #讀取一行
content = f.readlines() #讀取所有行萎庭,儲(chǔ)存在列表中霜医,每個(gè)元素是一行
如果以"w"或"a"方式打開(kāi),那么我們就可以寫(xiě)入文本:
f = open('test.txt','w')
f.write('i like apple') #將'i like apple'寫(xiě)入文件
如果想寫(xiě)入一行驳规,則需要在字符串末尾加上換行符
在UNIX系統(tǒng)中肴敛,換行符為"\n"
在windows系統(tǒng)中,換行符為"\r\n"
打開(kāi)文件端口將占用計(jì)算機(jī)資源吗购,因此医男,在讀寫(xiě)完成后
應(yīng)該及時(shí)的用文件對(duì)象的close()方法關(guān)閉文件:
f.close()
用法延伸:
r:以只讀方式打開(kāi)文件。文件的指針將會(huì)放在文件的開(kāi)頭捻勉。這是默認(rèn)模式
rb:以二進(jìn)制只讀方式打開(kāi)一個(gè)文件镀梭。文件指針將會(huì)放在文件的開(kāi)頭
r+:以讀寫(xiě)方式打開(kāi)一個(gè)文件。文件指針將會(huì)放在文件的開(kāi)頭
rb+:以二進(jìn)制讀寫(xiě)方式打開(kāi)一個(gè)文件踱启。文件指針將會(huì)放在文件的開(kāi)頭
w:以寫(xiě)入方式打開(kāi)一個(gè)文件报账。如果該文件已存在,則將其覆蓋埠偿。如果該文件不存在透罢,則創(chuàng)建新文件。
wb:以二進(jìn)制寫(xiě)入方式打開(kāi)一個(gè)文件胚想。如果該文件已存在琐凭,則將其覆蓋。如果該文件不存在浊服,則創(chuàng)建新文件统屈。
w+:以讀寫(xiě)方式打開(kāi)一個(gè)文件胚吁。如果該文件已存在,則將其覆蓋愁憔。如果該文件不存在腕扶,則創(chuàng)建新文件。
wb+:以二進(jìn)制讀寫(xiě)格式打開(kāi)一個(gè)文件吨掌。如果該文件已存在半抱,則將其覆蓋。如果該文件不存在膜宋,則創(chuàng)建新文件窿侈。
a:以追加方式打開(kāi)一個(gè)文件。如果該文件已存在秋茫,文件指針將會(huì)放在文件結(jié)尾史简。
也就是說(shuō),新的內(nèi)容將會(huì)被寫(xiě)入到已有內(nèi)容之后肛著。如果該文件不存在圆兵,則創(chuàng)建新文件來(lái)寫(xiě)入。
ab:以二進(jìn)制追加方式打開(kāi)一個(gè)文件枢贿。如果該文件已存在殉农,則文件指針將會(huì)放在文件結(jié)尾。
也就是說(shuō)局荚,新的內(nèi)容將會(huì)被寫(xiě)入到已有內(nèi)容之后超凳。如果該文件不存在,則創(chuàng)建新文件來(lái)寫(xiě)入耀态。
a+:以讀寫(xiě)方式打開(kāi)一個(gè)文件聪建。如果該文件已存在,文件指針將會(huì)放在文件的結(jié)尾茫陆。
文件打開(kāi)時(shí)會(huì)是追加模式。如果該文件不存在擎析,則創(chuàng)建新文件來(lái)讀寫(xiě)簿盅。
ab+:以二進(jìn)制追加方式打開(kāi)一個(gè)文件。如果該文件已存在揍魂,則文件指針將會(huì)放在文件結(jié)尾桨醋。
如果該文件不存在,則創(chuàng)建新文件用于讀寫(xiě)现斋。
來(lái)源鏈接:https://juejin.im/post/5ab22accf265da239a5fb172
2.上下文管理器
文件操作常常和上下文管理器一起使用喜最,上下文管理器(context manager)
用于規(guī)定某個(gè)對(duì)象的使用范圍,一旦進(jìn)入或者離開(kāi)該使用范圍庄蹋,則會(huì)有特殊操作被調(diào)用
比如為對(duì)象分配或者釋放內(nèi)存瞬内,上下文管理器可用于文件操作迷雪,對(duì)于文件操作來(lái)說(shuō)
我們需要在讀寫(xiě)結(jié)束時(shí)關(guān)閉文件,程序員經(jīng)常會(huì)忘記關(guān)閉文件虫蝶,無(wú)謂占用資源
上下文管理器可以在不需要文件的時(shí)候章咧,自動(dòng)關(guān)閉文件
下面時(shí)一段常規(guī)的文件操作程序:
#常規(guī)文件操作
f = open("new.txt","w")
print(f.closed) #檢查文件是否打開(kāi)
f.write("hello world!")
f.close()
print(f.closed) #打印True
如果我們加入上下文管理器的語(yǔ)法,就可以把程序改寫(xiě)為:
#使用上下文管理器
with open("new.txt","w") as f:
f.write("hello world!")
print(f.closed)
第二段程序就使用了with...as...結(jié)構(gòu)能真,上下文管理器有隸屬于它的程序塊
當(dāng)隸屬的程序塊執(zhí)行結(jié)束時(shí)赁严,也就是語(yǔ)句不再縮進(jìn)時(shí),上下文管理器就會(huì)自動(dòng)關(guān)閉文件
在程序中粉铐,我們調(diào)用了f.closed屬性來(lái)驗(yàn)證是否已經(jīng)關(guān)閉
通過(guò)上下文管理器疼约,我們相當(dāng)于用縮進(jìn)來(lái)表達(dá)文件對(duì)象的打開(kāi)范圍
對(duì)于復(fù)雜的程序來(lái)說(shuō),縮進(jìn)的存在能讓程序員更清楚的意識(shí)到文件在哪些階段打開(kāi)
減少忘記關(guān)閉文件的可能性
上面的上下文管理器基于f對(duì)象的exit()特殊方法
使用上下文管理器的語(yǔ)法時(shí)蝙泼,py會(huì)在進(jìn)入程序塊之前調(diào)用文件對(duì)象的enter()方法
在結(jié)束程序塊的時(shí)候調(diào)用文件對(duì)象的exit()方法
在文件對(duì)象的exit()中程剥,有self.close()語(yǔ)句
因此,在使用上下文管理器時(shí)踱承,我們就不必用明文關(guān)閉文件了
任何定義了enter()方法和exit()的對(duì)象都可以用于上下文管理器
下面倡缠,我們自定義一個(gè)類(lèi)Vow,并定義它的enter()方法和exit()方法
因此,由Vow類(lèi)的對(duì)象可以用于上下文管理器:
class Vow(object):
def __init__(self,text):
self.text = text
def __enter__(self):
self.text = "i say:" + self.text #增加前綴
return self #返回一個(gè)對(duì)象
def __exit__(self,exc_type,exc_value,traceback):
self.text = self.text + "!" #增加后綴
with Vow("I'm fine") as myVow:
print(myVow.text)
print(myVow.text)
i say:I'm fine
i say:I'm fine!
初始化對(duì)象時(shí)茎活,對(duì)象的text屬性是"I'm fine"我們可以看到
在進(jìn)入上下文和離開(kāi)上下文時(shí)昙沦,對(duì)象調(diào)用了enter()方法和exit()方法
從而造成對(duì)象的text屬性改變
enter()返回一個(gè)對(duì)象,上下文管理器會(huì)使用這一對(duì)象作為as所指變量
我們自定義的enter()返回的是self载荔,也就是新建的Vow類(lèi)對(duì)象本身
在enter()中盾饮,我們?yōu)閠ext屬性增加了前綴"i say:"
在exit()中,我們?yōu)閠ext屬性增加后綴"!"
值得注意的是懒熙,exit()有四個(gè)參數(shù)丘损,當(dāng)程序塊中出現(xiàn)異常時(shí)
exit()參數(shù)中exc_type,exc_value,traceback用于描述異常
我們可以根據(jù)這三個(gè)參數(shù)進(jìn)行相應(yīng)的處理,如果正常運(yùn)行結(jié)束工扎,則這三個(gè)參數(shù)都是None
3.pickle包
我們能把文本存于文件徘钥,但py中最常見(jiàn)的是對(duì)象,當(dāng)程序結(jié)束或者計(jì)算機(jī)關(guān)閉時(shí)
這些存在于內(nèi)存的對(duì)象會(huì)消失肢娘,那么呈础,我們能否把對(duì)象保存在磁盤(pán)上呢
利用pickle包就可以做到這一點(diǎn),英文里橱健,pickle是腌菜的意思
大航海時(shí)代的海員們常把蔬菜做成腌菜而钞,裝在罐頭里帶著走
py中的pickle也有類(lèi)似的意思,通過(guò)pickle包拘荡,我們可以把某個(gè)對(duì)象保存下來(lái)
再存成磁盤(pán)里的文件
實(shí)際上臼节,對(duì)象的存儲(chǔ)分為兩步,第一步,我們將對(duì)象在內(nèi)存里的數(shù)據(jù)直接抓取出來(lái)
轉(zhuǎn)換成一個(gè)有序的文本网缝,即所謂的序列化(Serialization),第二步
將文本存入文件巨税,等到需要時(shí),我們從文件中讀出文本途凫,再放入內(nèi)存
就可以獲得原有的對(duì)象垢夹,下面是一個(gè)具體的例子
首先是第一步序列化,將內(nèi)存中的對(duì)象轉(zhuǎn)換為文本流:
import pickle
class Bird(object):
have_feather = True
reproduction_method = 'egg'
summer = Bird() #創(chuàng)建對(duì)象
pickle_string = pickle.dumps(summer) #序列化對(duì)象
使用pickle包的dumps()方法可以將對(duì)象轉(zhuǎn)換成字符串的形式
隨后维费,我們用字節(jié)文本的存儲(chǔ)方法果元,將該字符串儲(chǔ)存在文件
繼續(xù)第二步
with open("summer.pkl","wb") as f:
f.write(pickle_string)
上面程序故意分成了兩步,以便更好的展示整個(gè)過(guò)程
其實(shí)犀盟,我們可以使用dump()的方法而晒,一次完成兩步:
import pickle
class Bird(object):
have_feather = True
reproduction_method = 'egg'
summer = Bird()
with open("summer.pkl","wb") as f:
pickle.dump(summer,f) #有序化并保存對(duì)象
對(duì)象summer將存儲(chǔ)在文件summer.pkl中
有了這個(gè)文件,我們就可以在必要的時(shí)候讀取對(duì)象了
讀取對(duì)象與存儲(chǔ)對(duì)象的過(guò)程正好相反阅畴,首先倡怎,我們從文件中讀取文本
然后使用pickle的loads()方法,將字符串形式的文本轉(zhuǎn)換為對(duì)象
我們也可以使用pickle的load()方法贱枣,將上面兩步合并
有的時(shí)候监署,僅僅是反向恢復(fù)還不夠,對(duì)象依賴(lài)于它的類(lèi)
所以py在創(chuàng)建對(duì)象時(shí)纽哥,需要找到相應(yīng)的類(lèi)
因此當(dāng)我們從文本中讀取對(duì)象時(shí)钠乏,程序中必須已經(jīng)定義過(guò)類(lèi)
對(duì)于py總是存在的內(nèi)置類(lèi),如列表春塌,詞典晓避,字符串等
不需要再在程序中定義,但是對(duì)于用戶(hù)自定義的類(lèi)只壳,就必須要先定義類(lèi)
然后才能在文件中載入該類(lèi)的對(duì)象
下面是一個(gè)讀取對(duì)象的例子:
import pickle
class Bird(object):
have_feather = True
reproduction_method = 'egg'
summer = Bird()
with open("summer.pkl","rb") as f:
summer = pickle.load(f)
print(summer.have_feather) #打印True