過去的代碼都是未經(jīng)測試的代碼渴频。
目錄
無論是哪種編程語言芽丹,IO
操作都是非常重要的部分。I
即Input
(輸入)卜朗,O
即Output
(輸出)拔第。
IO
操作一般分為以下兩種:
-
磁盤IO: 即在磁盤上
讀寫
文件。讀文件
是指將文件內(nèi)容從磁盤讀入內(nèi)存场钉,寫文件
是指將內(nèi)存中的內(nèi)容寫到磁盤蚊俺。 -
網(wǎng)絡(luò)IO: 即文件在網(wǎng)絡(luò)上傳輸。網(wǎng)絡(luò)傳輸一般會有兩種角色逛万,分別是
服務(wù)端
(如HTTP Server
)和客戶端
(如瀏覽器
)泳猬。
本節(jié)我們主要介紹磁盤IO
,即文件讀寫
泣港。
1暂殖,open
函數(shù)介紹
要想讀寫文件价匠,首先要打開
一個(gè)文件当纱。
Python 中的內(nèi)建函數(shù)open
用來打開一個(gè)文件,我們可以使用help(open)
踩窖,來查看open
函數(shù)的原型坡氯,如下:
open(file, mode='r',
buffering=-1, encoding=None,
errors=None, newline=None,
closefd=True, opener=None)
該函數(shù)成功調(diào)用時(shí)會返回一個(gè)流stream
,用于讀寫文件等操作洋腮;發(fā)生錯誤時(shí)會拋出IOError
異常箫柳。
被打開的文件占用了系統(tǒng)資源,使用完后要記得close
啥供,否則會浪費(fèi)系統(tǒng)資源悯恍。
不管以讀模式
打開文件,還是以寫模式
打開文件伙狐,成功打開一個(gè)文件后涮毫,這個(gè)可操作文件的流
的內(nèi)部都有一個(gè)隱含的指針
,一般這個(gè)指針會指向文件開頭
或者文件末尾
的位置贷屎,表示從文件的哪個(gè)位置讀寫文件罢防。
可以看到,該函數(shù)支持8 個(gè)參數(shù)唉侄,但最重要的是前兩個(gè)參數(shù):
-
file
:是指要打開的文件的路徑 -
mode
:是指以什么模式打開文件咒吐,要用引號
引住
mode
參數(shù)支持的模式(默認(rèn)為讀文本
模式,即rt
)如下:
-
r
:以讀模式
打開文件(默認(rèn)方式),指針在文件開頭 -
w
:以寫模式
打開文件恬叹,如果件已存在候生,則內(nèi)容會被清空(指針在文件開頭);如果文件不存在绽昼,則會創(chuàng)建新文件 -
x
:創(chuàng)建一個(gè)新文件陶舞,并以寫模式
打開,指針在文件開頭绪励,如果文件已存在肿孵,則拋出FileExistsError
異常 -
a
:以寫模式
打開文件,如果文件已有內(nèi)容疏魏,在寫入內(nèi)容時(shí)停做,會追加
到文件末尾(指針在文件末尾) -
b
:以二進(jìn)制模式
打開文件,一般用于讀寫二進(jìn)制文件大莫,如圖片蛉腌,視頻等 -
t
:以文本模式
打開文件(默認(rèn)方式),一般用于讀寫文本文件 -
+
:以讀寫模式
打開文件只厘,指針在文件開頭
這些模式還可以組合使用烙丛,常見的組合如下:
-
rb
:以二進(jìn)制模式
打開一個(gè)文件,用于只讀
-
r+
:打開一個(gè)文件羔味,用于讀寫
-
rb+
:以二進(jìn)制模式
打開一個(gè)文件河咽,用于讀寫
-
wb
:以二進(jìn)制模式
打開一個(gè)文件,用于寫
-
w+
:打開一個(gè)文件赋元,用于讀寫
-
wb+
: 以二進(jìn)制模式
打開一個(gè)文件忘蟹,用于讀寫
-
ab
: 以二進(jìn)制模式
打開一個(gè)文件,用于追加
-
a+
:打開一個(gè)文件用于讀寫
搁凸,指針在文件末尾
-
ab+
:以二進(jìn)制模式
打開一個(gè)文件媚值,用于讀寫
,指針在文件末尾
2护糖,open
函數(shù)示例
如下代碼褥芒,成功打開文件./1.txt
:
f = open('./1.txt')
通過type(f)
查看open
函數(shù)的返回值的類型:
>>> type(file)
<class '_io.TextIOWrapper'>
可看到,其返回值類型為_io.TextIOWrapper
嫡良。
我們用dir(f)
來查看對象 f
支持的屬性和方法:
>>> dir(file)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__',
'__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__lt__',
'__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable',
'_finalizing',
'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno',
'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read',
'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell',
'truncate', 'writable', 'write', 'writelines']
可以通過help(f.方法名)
來查看每個(gè)方法的幫助手冊锰扶,也可以使用help(f)
來查看該對象的所有屬性和方法,及其簡介皆刺。
我們來看一下常用方法的作用:
-
mode
:打開文件時(shí)的模式 -
name
:被打開的文件名 -
close
:關(guān)閉文件流少辣,并刷新緩沖區(qū)中的內(nèi)容,之后不能再操作文件 -
closed
:文件流是否已關(guān)閉 -
flush
:刷新寫緩沖區(qū)羡蛾,只寫流
與非阻塞流
不適用 -
read
:讀入文件內(nèi)容 -
readable
:是否可讀 -
readline
:讀入一行內(nèi)容 -
readlines
:讀入文件所有的行漓帅,直至文件末尾 -
seek
:移動文件指針的位置 -
seekable
:文件指針是否可被移動 -
tell
:返回文件指針
當(dāng)前位置 -
truncate
:截?cái)辔募?nèi)容 -
writable
:是否可寫 -
write
:向文件中寫入內(nèi)容 -
writelines
:向文件中寫入多行
3,關(guān)閉系統(tǒng)資源
正確的調(diào)用close()
函數(shù)是關(guān)鍵的。
在成功打開一個(gè)文件后忙干,對該文件進(jìn)行操作(讀寫)時(shí)器予,有可能發(fā)生異常。
比如我們打開的文件只能用來寫
捐迫,如果用來讀
乾翔,則會發(fā)生異常:
>>> f = open('1.txt', 'w') # 用只讀模式打開文件
>>> f.readable() # 查看文件是否可讀
False # 返回 False,表示不可讀
>>> f.read() # 讀文件施戴,發(fā)生異常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
io.UnsupportedOperation: not readable
如果反浓,我們將這段代碼寫在文件中:
#! /usr/bin/env python3
f = open('1.txt', 'w')
f.read()
f.close()
用python3
來執(zhí)行,結(jié)果如下:
$ python3 Test.py
Traceback (most recent call last):
File "Test.py", line 4, in <module>
f.read()
io.UnsupportedOperation: not readable
可以看到赞哗,在執(zhí)行到f.read()
這句代碼的時(shí)候雷则,程序異常退出,那么后邊的f.close()
就沒有執(zhí)行到肪笋,這就導(dǎo)致程序執(zhí)行不夠完整月劈,系統(tǒng)資源沒有關(guān)閉。
這時(shí)藤乙,我們可以用try...finally
來處理猜揪,如下:
#! /usr/bin/env python3
f = open('1.txt', 'w')
try:
f.read()
except Exception as e:
print('read file err:%s' % e)
finally:
f.close()
print('file closed')
上面代碼的執(zhí)行結(jié)果如下:
$ python3 Test.py
read file err:not readable
file closed
我們將f.close()
這句代碼放在了finally
代碼塊中,這樣坛梁,不管遇到什么情況而姐,f.close()
這句話總會被執(zhí)行,就不會導(dǎo)致系統(tǒng)資源泄漏的問題罚勾。
4毅人,with
語句使用
為了確保系統(tǒng)資源能夠關(guān)閉,Python 中提供了with
語句尖殃,能夠讓我們更加安全方面的使用open
函數(shù),而不用關(guān)心資源關(guān)閉的問題划煮。
with
語句也叫上下文管理器
送丰,有了with
語句,我們可以這樣使用open
函數(shù):
with open('./1.txt') as f:
print(f.read())
這樣的代碼弛秋,不管在with
語句塊內(nèi)出現(xiàn)怎樣的異常器躏,close
函數(shù)都會被調(diào)用,而我們也不需要自己調(diào)用蟹略。
使用with
語句登失,就不再需要使用try...finally
語句,也使得代碼更加簡潔挖炬。
需要特別注意的是揽浙,這里的f
只能在with
語句塊中使用,一旦離開with
語句塊,f
就被關(guān)閉了馅巷。如果在with
語句塊之外使用f
進(jìn)行讀寫等操作膛虫,將出現(xiàn)異常。
如下代碼中钓猬,f.closed
將返回True
:
with open('./1.txt') as f:
pass
f.closed # True
5稍刀,with
語句原理
為什么open
函數(shù)能夠使用with
語句?
實(shí)際上open
函數(shù)能夠使用with
語句的原因取決于open
的返回值的類型
敞曹。我們知道账月,open
的返回值的類型為_io.TextIOWrapper
,而這個(gè)類中有兩個(gè)方法澳迫,__enter__
方法和__exit__
方法捶障。
我們再來看下with
語句的格式:
with ... as ... :
pass
with
關(guān)鍵字的后邊是一個(gè)表達(dá)式
,as
后邊是一個(gè)變量名纲刀,表達(dá)式的計(jì)算結(jié)果會賦值給as
后邊的變量项炼。
Python 規(guī)定,只要一個(gè)類中有__enter__
和__exit__
方法示绊,就可以使用with
語句锭部。with
語句后邊的表達(dá)式執(zhí)行完畢后,就會執(zhí)行__enter__
方法面褐,在退出with
語句塊時(shí)拌禾,會執(zhí)行__exit__
方法。
我們自己編寫一個(gè)測試類展哭,使其能夠使用with
語句:
#! /usr/bin/env python3
class TestWith:
def __init__(self):
print('執(zhí)行__init__')
def __enter__(self):
print('執(zhí)行__enter__')
def __exit__(self, exc_type, exc_val, exc_tb):
print('執(zhí)行__exit__')
print('exc_type is %s' % exc_type)
print('exc_val is %s' % exc_val)
print('exc_tb is %s' % exc_tb)
再該類中有三個(gè)函數(shù):
-
__init__
:構(gòu)造函數(shù)湃窍,創(chuàng)建類的對象時(shí)調(diào)用 -
__enter__
:進(jìn)入with
語句塊時(shí)會調(diào)用 -
__exit__
:離開with
語句塊時(shí)會調(diào)用
其中__exit__
方法有三個(gè)參數(shù):
-
exc_type
:with
語句塊中的代碼發(fā)生異常時(shí)的異常類型
-
exc_val
:發(fā)生異常時(shí)的異常值
-
exc_tb
:發(fā)生異常時(shí)的traceback
類的對象
我們這樣使用這個(gè)類:
with TestWith() as t:
print('test with')
用python3
來執(zhí)行,結(jié)果如下:
$ python3 Test.py
執(zhí)行__init__
執(zhí)行__enter__
test with
執(zhí)行__exit__
exc_type is None
exc_val is None
exc_tb is None
可以看到執(zhí)行步驟是這樣的:
- 生成該類的對象匪傍,執(zhí)行
__init__
方法 - 進(jìn)入
with
語句塊您市,執(zhí)行__enter__
方法 - 執(zhí)行
with
語句塊中的代碼 - 退出
with
語句塊,執(zhí)行__exit__
方法
因?yàn)?code>with 語句塊中沒有發(fā)生異常役衡,所以__exit__
方法中的 exc_type
茵休,exc_val
,exc_tb
三個(gè)參數(shù)均為None
手蝎。
下面再示范一個(gè)with
語句塊中出現(xiàn)異常的代碼:
with TestWith() as t:
print('test with1...')
1 / 0 # 除數(shù)為 0榕莺,拋出異常
print('test with2...')
該代碼的執(zhí)行結(jié)果如下:
$ python3 Test.py
執(zhí)行__init__
執(zhí)行__enter__
test with1...
執(zhí)行__exit__
exc_type is <class 'ZeroDivisionError'>
exc_val is division by zero
exc_tb is <traceback object at 0x7fe8b7c98888>
Traceback (most recent call last):
File "Test.py", line 27, in <module>
1 / 0
ZeroDivisionError: division by zero
通過上面的執(zhí)行結(jié)果可以看到,在執(zhí)行1 / 0
之前棵介,我們不用多說钉鸯。在執(zhí)行到1 / 0
時(shí),出現(xiàn)異常邮辽,然后會執(zhí)行__exit__
方法唠雕。
在執(zhí)行結(jié)果中贸营,我們能看到 exc_type
,exc_val
及塘,exc_tb
三個(gè)參數(shù)的值莽使,最后拋出Traceback
異常。
with
語句中笙僚,拋出異常的語句1 / 0
之后的代碼不會再執(zhí)行芳肌。
(完。)
推薦閱讀:
Python 簡明教程 ---19肋层,Python 類與對象
Python 簡明教程 ---20亿笤,Python 類中的屬性與方法
Python 簡明教程 ---21,Python 繼承與多態(tài)
Python 簡明教程 ---22栋猖,Python 閉包與裝飾器
Python 簡明教程 ---23净薛,Python 異常處理