中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

給Python學(xué)習(xí)者的文件讀寫指南(含基礎(chǔ)與進(jìn)階)

2018-10-16    來源:raincent

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

對于初學(xué)者來說,一份詳盡又清晰明白的指南很重要。今天跟大家一起,好好學(xué)習(xí)Python文件讀寫的內(nèi)容,這部分內(nèi)容特別常用,掌握后對工作和實戰(zhàn)都大有益處。學(xué)習(xí)是循序漸進(jìn)的過程,欲速則不達(dá)。文章較長,建議大家收藏,以備復(fù)習(xí)查閱哦。

1、如何將列表數(shù)據(jù)寫入文件?
2、如何從文件中讀取內(nèi)容?
3、多樣需求的讀寫任務(wù)
4、從with語句到上下文管理器

如何將列表數(shù)據(jù)寫入文件?

首先,我們來看看下面這段代碼,并思考:這段代碼有沒有問題,如果有問題的話,要怎么改?

li = ['python',' is',' a',' cat']
with open('test.txt','w') as f:
f.write(li)

現(xiàn)在公布答案,這段代碼會報錯:

TypeError Traceback (most recent call last)
in ()
1 with open('test.txt','w') as f:
----> 2 f.write(li)

TypeError: write() argument must be str, not list
以上代碼的想法是將list列表內(nèi)容寫入txt文件中,但是報錯 TypeError: write() argument must be str。就是說,write()方法必須接受字符串(str)類型的參數(shù)。

Python中內(nèi)置了str()方法,可以返回字符串版本的對象(Return a string version of object)。所以,上面的例子中,我們試試把 f.write(li) 改為 f.write(str(li)) ,先做一下字符串類型的轉(zhuǎn)化看看。代碼略。

這次沒有報錯了,但是打開文件就傻眼了吧,寫入的內(nèi)容是“['python',' is',' a',' cat']”。怎么才能寫成“python is a cat”呢?

文件寫操作還有一個writelines()方法,它接收的參數(shù)是由字符串組成的序列(sequence),實際寫入的效果是將全部字符串拼接在一起。字符串本身也是一種序列,所以當(dāng)參數(shù)是字符串的時候,writelines()方法等價于write()。

# 以下3種寫法等價,都是寫入字符串“python is a cat”
In [20]: with open('test.txt','w') as f:
...: f.writelines(['python',' is',' a',' cat'])
...: f.writelines('python is a cat')
...: f.write('python is a cat')

# 以下2種寫法等價,都是寫入列表的字符串版本“['python',' is',' a',' cat']”
In [21]: with open('test.txt','w') as f:
...: f.write(str(['python',' is',' a',' cat']))
...: f.writelines(str(['python',' is',' a',' cat']))

# 作為反例,以下寫法都是錯誤的:

In [22]: with open('test.txt','w') as f:
...: f.writelines([2018,'is','a','cat']) # 含非字符串
...: f.write(['python','is','a','cat']) # 非字符串
由上可知,當(dāng)多段分散的字符串存在于列表中的時候,要用writelines()方法,如果字符串是一整段,那直接使用write()方法。如果要以整個列表的形式寫入文件,就使用str()方法做下轉(zhuǎn)化。

這個問題還沒結(jié)束,如果列表中就是有元素不是字符串,而且要把全部元素取出來,怎么辦呢?

那就不能直接使用write()和writelines()了,需要先用for循環(huán),把每個元素取出來,逐一str()處理。

In [37]: content=[1,' is',' everything']
In [38]: with open('test.txt','w') as f:
...: for i in content:
...: f.write(str(i))

需要注意的是,writelines()不會自動換行。如果要實現(xiàn)列表元素間的換行,一個辦法是在每個元素后面加上換行符“\n”,如果不想改變元素,最好是用for循環(huán),在寫入的時候加在末尾:for i in content: f.writelines(str(i)+“\n”).

引申一下,經(jīng)過實驗,數(shù)字及元祖類型也可以作為write()的參數(shù),不需轉(zhuǎn)化。但是dict字典類型不可以,需要先用str()處理一下。字典類型比較特殊,最好是用json.dump()方法寫到文件,具體操作方法以及注意事項,請看喵喵之前發(fā)的《假期玩得開心也不忘充電,學(xué)習(xí)Python操作JSON,網(wǎng)絡(luò)數(shù)據(jù)交換不用愁》.

總結(jié)一下,write()接收字符串參數(shù),適用于一次性將全部內(nèi)容寫入文件;writelines()接收參數(shù)是由字符串組成的序列,適用于將列表內(nèi)容逐行寫入文件。str()返回Python對象的字符串版本,使用需注意。

如何從文件中讀取內(nèi)容?

從文件中讀取內(nèi)容有如下方法:

file.read([size])
從文件讀取指定的字節(jié)數(shù),如果未給定或為負(fù)則讀取所有。

file.readline([size])

讀取整行,包括 "\n" 字符。

file.readlines([sizeint])
讀取所有行并返回列表,若給定sizeint>0,則是設(shè)置一次讀多少字節(jié),這是為了減輕讀取壓力。
簡而言之,在不傳參數(shù)的情況下,read()對應(yīng)write(),讀取全部內(nèi)容;readlines()對應(yīng)writelines(),讀取全部內(nèi)容(含換行符)并以列表形式返回,每個換行的內(nèi)容作為列表的一個元素。

In [47]: with open('test.txt','r') as f:
...: print(f.read())
1 is everything.
python is a cat.
this is the end.

In [48]: with open('test.txt','r') as f:
...: print(f.readlines())
['1 is everything.\n', 'python is a cat.\n', 'this is the end.']

但是,以上兩個方法有個缺點,當(dāng)文件過大的時候,一次性讀取太多內(nèi)容,會對內(nèi)存造成極大壓力。讀操作還有一個readline()方法,可以逐行讀取。

In [49]: with open('test.txt','r') as f:
...: print(f.readline())
1 is everything.
readline()

讀取第一行就返回,再次調(diào)用f.readline(),會讀取下一行。

喵喵,是否感覺跟《超強(qiáng)匯總:學(xué)習(xí)Python列表,只需這篇文章就夠了》學(xué)習(xí)過的生成器很像,需要不停調(diào)用next()獲取下一行。

這么看來,readline()太笨拙了。那么,有什么辦法可以優(yōu)雅地讀取文件內(nèi)容呢?

回過頭來看readlines()方法,它返回的是一個列表。這不奇怪么,好端端的內(nèi)容為啥要返回成列表呢?

再想想writelines()方法,把字符串列表寫入文件正是這家伙干的事,readlines()方法恰恰是它的逆操作!而writelines()方法要配合for循環(huán),所以我們把readlines()與for循環(huán)結(jié)合,看看會怎樣。

In [61]: with open('test.txt','r') as f:
...: for line in f.readlines():
...: print(line)
1 is everything.

python is a cat.

this is the end.

# 讀取內(nèi)容包含換行符,所以要strip()去掉換行符
In [62]: with open('test.txt','r') as f:
...: for line in f.readlines():
...: print(line.strip())
1 is everything.
python is a cat.
this is the end.
總結(jié)一下,readline()比較雞肋,不咋用;read()適合讀取內(nèi)容較少的情況,或者是需要一次性處理全部內(nèi)容的情況;而readlines()用的較多,比較靈活,因為for循環(huán)是一種迭代器,每次加載部分內(nèi)容,既減少內(nèi)存壓力,又方便逐行對數(shù)據(jù)處理。

多樣需求的讀寫任務(wù)

前兩部分講了文件讀寫的幾大核心方法,它們能夠起作用的前提就是,需要先打開一個文件對象,因為只有在文件操作符的基礎(chǔ)上才可以進(jìn)行讀或者寫的操作。

打開文件用的是open()方法,所以我們再繼續(xù)講講這個方法。open() 方法用于打開一個文件,并返回文件對象,在對文件進(jìn)行處理過程都需要使用到這個函數(shù),如果該文件無法被打開,會拋出 OSError。

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
open()方法的參數(shù)里file(文件)是必需的,其它參數(shù)最常用的是mode(模式)和encoding(編碼)。

先說說encoding,一般來說,打開文件的編碼方式以操作系統(tǒng)的默認(rèn)編碼為準(zhǔn),中文可能會出現(xiàn)亂碼,需要加encoding='utf-8'。

In [63]: with open('test.txt','r') as f:
...: for line in f.readlines():
...: print(line.strip())
-----------------------
UnicodeDecodeError Traceback (most recent call last)
in ()
1 with open('test.txt','r') as f:
----> 2 for line in f.readlines():
3 print(line.strip())
UnicodeDecodeError: 'gbk' codec can't decode byte 0xa4 in position 26: illegal multibyte sequence

In [65]: with open('test.txt','r',encoding='utf-8') as f:
...: for line in f.readlines():
...: print(line.strip())
愛貓貓
python is a cat.
再說mode,它指定文件打開的模式。

'r': 以只讀模式打開(缺省模式,必須保證文件存在)
'w':以只寫模式打開。若文件存在,則清空文件,然后重新創(chuàng)建;若不存在,則新建
'a':以追加模式打開。若文件存在,則會追加到文件的末尾;若文件不存在,則新建

常見的mode組合

'r'或'rt': 默認(rèn)模式,文本讀模式
'w'或'wt':以文本寫模式打開(打開前文件被清空)
'rb': 以二進(jìn)制讀模式打開
'ab': 以二進(jìn)制追加模式打開
'wb': 以二進(jìn)制寫模式打開(打開前文件被清空)
'r+': 以文本讀寫模式打開,默認(rèn)寫的指針開始指在文件開頭, 因此會覆寫文件
'w+': 以文本讀寫模式打開(打開前文件被清空)
'a+': 以文本讀寫模式打開(只能寫在文件末尾)
'rb+': 以二進(jìn)制讀寫模式打開
'wb+': 以二進(jìn)制讀寫模式打開(打開前被清空)
'ab+': 以二進(jìn)制讀寫模式打開
喵喵,初看起來,模式很多,但是,它們只是相互組合罷了。建議記住最基本的w、r、a,遇到特殊場景,再翻看一下就好了。

從with語句到上下文管理器

基礎(chǔ)部分講完了,下面是進(jìn)階部分。知其然,更要知其所以然。

1、with語句是初學(xué)者必會常識

首先,要解釋一下為啥前文直接就用了with語句。with語句是讀寫文件時的優(yōu)雅寫法,這已經(jīng)默認(rèn)是Python初學(xué)者必會的常識了。如果你還不會,先看看用和不用with語句的對比:

# 不用with語句的正確寫法
try:
f = open('test.txt','w')
f.writelines(['python',' is',' a',' cat'])
finally:
if f:
f.close()

# 使用with語句的正確寫法
with open('test.txt','w') as f:
f.writelines(['python',' is',' a',' cat'])
因為文件對象會占用操作系統(tǒng)的資源,并且操作系統(tǒng)同一時間能打開的文件數(shù)量是有限的,所以open()方法之后一定要調(diào)用close()方法。另外,讀寫操作可能出現(xiàn)IO異常的情況,所以要加try…finally,保證無論如何,都會調(diào)用到close()方法。

這樣寫萬無一失,但是實在繁瑣,一不小心還可能漏寫或者寫錯。而with語句會保證調(diào)用close(),只需一行代碼,簡直不要太優(yōu)雅!所以,with語句是Python初學(xué)者必會技能。

2、什么是上下文管理器?

下面,重頭戲來了,什么是上下文管理器(context manager)?

上下文管理器是這樣一個對象:它定義程序運行時需要建立的上下文,處理程序的進(jìn)入和退出,實現(xiàn)了上下文管理協(xié)議,即在對象中定義了 __enter__() 和 __exit__() 方法。

__enter__():進(jìn)入運行時的上下文,返回運行時上下文相關(guān)的對象,with 語句中會將這個返回值綁定到目標(biāo)對象。

__exit__(exception_type, exception_value, traceback):退出運行時的上下文,定義在塊執(zhí)行(或終止)之后上下文管理器應(yīng)該做什么。它可以處理異常、清理現(xiàn)場或者處理 with 塊中語句執(zhí)行完成之后需要處理的動作。
注意enter和exit的前后有兩個下劃線,Python中自帶了很多類似的方法,它們是很神秘又很強(qiáng)大的存在,江湖人常常稱其為“黑魔法”。例如,迭代器協(xié)議就實現(xiàn)了__iter__方法。

在Python的內(nèi)置類型中,很多類型都是支持上下文管理協(xié)議的,例如file,thread.LockType,threading.Lock等等。

上下文管理器無法獨立使用,它們要與with相結(jié)合,with語句可以在代碼塊運行前進(jìn)入一個運行時上下文(執(zhí)行_enter_方法),并在代碼塊結(jié)束后退出該上下文(執(zhí)行__exit__方法)。

with 語句適用于對資源進(jìn)行訪問的場合,確保不管使用過程中是否發(fā)生異常都會執(zhí)行必要的“清理”操作,釋放資源,比如文件使用后自動關(guān)閉、線程中鎖的自動獲取和釋放等。

3、自定義上下文管理器

除了Python的內(nèi)置類型,任何人都可以定義自己的上下文管理器。下面是一個示例:

class OpenFile(object):
def __init__(self,filename,mode):
self.filename=filename
self.mode=mode
def __enter__(self):
self.f=open(self.filename,self.mode)
self.f.write("enter now\n")
return self.f #作為as說明符指定的變量的值
def __exit__(self,type,value,tb):
self.f.write("exit now")
self.f.close()
return False #異常會被傳遞出上下文
with OpenFile('test.txt','w') as f:
f.write('Hello World!\n')
最終寫入文件的結(jié)果是:

enter now
Hello World!
exit now
上下文管理器必須同時提供 __enter__() 和 _exit_() 方法的定義,缺少任何一個都會導(dǎo)致 AttributeError。

上下文管理器在執(zhí)行過程中可能會出現(xiàn)異常,_exit_() 的返回值會決定異常的處理方式:返回值等于 False,那么這個異常將被重新拋出到上層;返回值等于 True,那么這個異常就被忽略,繼續(xù)執(zhí)行后面的代碼。__exit()__ 有三個參數(shù)(exception_type, exception_value, traceback),即是異常的相關(guān)信息。

4、contextlib實現(xiàn)上下文管理器

上例中,自定義上下文管理器的寫法還是挺繁瑣的,而且只能用于類級別。為了更好地輔助上下文管理,Python 內(nèi)置提供了 contextlib 模塊,進(jìn)而可以很方便地實現(xiàn)函數(shù)級別的上下文管理器。

該模塊本質(zhì)上是通過裝飾器(decorators)和生成器(generators)來實現(xiàn)上下文管理器,可以直接作用于函數(shù)/對象,而不用去關(guān)心 __enter__() 和 __exit()__ 方法的具體實現(xiàn)。

先把上面的例子改造一下,然后我們再對照著解釋:

from contextlib import contextmanager

@contextmanager
def open_file(name):
ff = open(name, 'w')
ff.write("enter now\n")
try:
yield ff
except RuntimeError:
pass
ff.write("exit now")
ff.close()

with open_file('test.txt') as f:
f.write('Hello World!\n')

contextmanager是要使用的裝飾器,yield關(guān)鍵字將普通的函數(shù)變成了生成器。yield的返回值(ff)等于上例__enter__()的返回值,也就是as語句的值(f),而yield前后的內(nèi)容,分別是_enter_() 和 _exit_() 方法里的內(nèi)容。

使用contextlib,可以避免類定義、_enter_() 和 __exit()__方法,但是需要我們捕捉可能的異常(例如,yield只能返回一個值,否則會導(dǎo)致異常 RuntimeError),所以try…except語句不能忽略。

標(biāo)簽: 代碼 網(wǎng)絡(luò)

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:關(guān)于機(jī)器學(xué)習(xí),你應(yīng)該知道的3個熱門專業(yè)術(shù)語

下一篇:做數(shù)據(jù)科學(xué)如烤蛋糕?不服來看