Python:文件的讀寫與with...as語句


1.with open() as file

Python內置了讀寫文件的函數,用法和C是兼容的。在磁盤上讀寫文件的功能都是由操作系統提供的,現代操作系統不允許普通的程序直接操作磁盤,所以,讀寫文件就是請求操作系統打開一個文件對象(通常稱為文件描述符),然后,通過操作系統提供的接口從這個文件對象中讀取數據(讀文件),或者把數據寫入這個文件對象(寫文件)。

使用python內置的open()函數,open('路徑+文件名', '打開模式') ,第二個參數決定了打開文件的模式:只讀r,寫入w,追加a等。所有可取值見如下的完全列表。這個參數是非強制的,默認為r。

模式 描述
r 以只讀方式打開文件。文件的指針將會放在文件的開頭。這是默認模式。
rb 以二進制格式打開一個文件用於只讀。文件指針將會放在文件的開頭。這是默認模式。
r+ 打開一個文件用於讀寫。文件指針將會放在文件的開頭。
rb+ 以二進制格式打開一個文件用於讀寫。文件指針將會放在文件的開頭。
w 打開一個文件只用於寫入。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
wb 以二進制格式打開一個文件只用於寫入。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
w+ 打開一個文件用於讀寫。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
wb+ 以二進制格式打開一個文件用於讀寫。如果該文件已存在則將其覆蓋。如果該文件不存在,創建新文件。
a 打開一個文件用於追加。如果該文件已存在,文件指針將會放在文件的結尾。也就是說,新的內容將會被寫入到已有內容之后。如果該文件不存在,創建新文件進行寫入。
ab 以二進制格式打開一個文件用於追加。如果該文件已存在,文件指針將會放在文件的結尾。也就是說,新的內容將會被寫入到已有內容之后。如果該文件不存在,創建新文件進行寫入。
a+ 打開一個文件用於讀寫。如果該文件已存在,文件指針將會放在文件的結尾。文件打開時會是追加模式。如果該文件不存在,創建新文件用於讀寫。
ab+ 以二進制格式打開一個文件用於追加。如果該文件已存在,文件指針將會放在文件的結尾。如果該文件不存在,創建新文件用於讀寫。

如果文件打開成功,調用read()方法可以一次讀取文件的全部內容,並把內容讀到內存,用一個str對象表示;

文件使用完畢后必須關閉,因為文件對象會占用操作系統的資源,並且操作系統同一時間能打開的文件數量也是有限的,所以最后要調用close()方法關閉文件;

由於文件讀寫時都可能產生IOError,為了保證無論是否出錯都能正確地關閉文件,可以用 try...finally 來實現:

try: f = open('/path/to/file', 'r') print(f.read()) finally: if f: f.close()
View Co

雖然這段代碼運行良好,但是太冗長了。這時候就是with一展身手的時候了。除了有更優雅的語法,with還可以很好的處理上下文環境產生的異常:

with open('/path/to/file', 'r') as f: print(f.read())
with語句

♦ 調用read()會一次性讀取文件的全部內容,如果文件有10G,內存就爆了,所以,要保險起見,可以反復調用read(size)方法,每次最多讀取size 個字節的內容。另外,調用readline()可以每次讀取一行內容,調用readlines()一次讀取所有內容並按行返回要根據需要決定怎么調用。如果文件很小,read()一次性讀取最方便;如果不能確定文件大小,反復調用read(size)比較保險;如果是配置文件,調用readlines()最方便:

for line in f.readlines():
    print(line)

♦ 寫文件同樣:

with open('file', 'w') as f:
    f.write('Hello, world!')

當我們寫文件時,操作系統往往不會立刻把數據寫入磁盤,而是放到內存緩存起來,空閑的時候再慢慢寫入。只有調用close()方法時,操作系統才保證把沒有寫入的數據全部寫入磁盤。忘記調用close()的后果是數據可能只寫了一部分到磁盤,剩下的丟失了。

2.理解with語句

—with語句是什么?
有一些任務,可能事先需要設置,事后做清理工作。對於這種場景,Python的with語句提供了一種非常方便的處理方式。一個很好的例子是文件處理,你需要獲取一個文件句柄,從文件中讀取數據,然后關閉文件句柄。
—with如何工作?
這看起來充滿魔法,但不僅僅是魔法,Python對with的處理還很聰明。基本思想是with所求值的對象必須有一個__enter__()方法,一個__exit__()方法。
緊跟with后面的語句被求值后,返回對象的__enter__()方法被調用,這個方法的返回值將被賦值給as后面的變量。當with后面的代碼塊全部被執行完之后,將調用前面返回對象的__exit__()方法。

下面例子可以具體說明with如何工作:

#!/usr/bin/env python # with_example01.py
 
class Sample: def __enter__(self): print "In __enter__()"
        return "Foo"
 
    def __exit__(self, type, value, trace): print "In __exit__()"
 
def get_sample(): return Sample() with get_sample() as sample: print "sample:", sample
View Code

運行代碼,輸出如下:

In __enter__()
sample: Foo
In __exit__()

正如我們所看到的,
1. __enter__()方法被執行
2. __enter__()方法返回的值 - 這個例子中是"Foo",賦值給變量'sample'
3. 執行代碼塊,打印變量"sample"的值為 "Foo"
4. __exit__()方法被調用
with真正強大之處是它可以處理異常。可能你已經注意到Sample類的__exit__方法有三個參數- val, type 和 trace。 這些參數在異常處理中相當有用。我們來改一下代碼,看看具體如何工作的。

#!/usr/bin/env python # with_example02.py
 
class Sample: def __enter__(self): return self def __exit__(self, type, value, trace): print "type:", type print "value:", value print "trace:", trace def do_something(self): bar = 1/0 return bar + 10 with Sample() as sample: sample.do_something()
View Code

這個例子中,with后面的get_sample()變成了Sample()。這沒有任何關系,只要緊跟with后面的語句所返回的對象有__enter__()和__exit__()方法即可。此例中,Sample()的__enter__()方法返回新創建的Sample對象,並賦值給變量sample。
代碼執行后:

 1 bash-3.2$ ./with_example02.py  2 type: <type 'exceptions.ZeroDivisionError'>
 3 value: integer division or modulo by zero  4 trace: <traceback object at 0x1004a8128>
 5 Traceback (most recent call last):  6   File "./with_example02.py", line 19, in <module>
 7  sample.do_something()  8   File "./with_example02.py", line 15, in do_something  9     bar = 1/0 10 ZeroDivisionError: integer division or modulo by zero
View Code

實際上,在with后面的代碼塊拋出任何異常時,__exit__()方法被執行。正如例子所示,異常拋出時,與之關聯的type,value和stack trace傳給__exit__()方法,因此拋出的ZeroDivisionError異常被打印出來了。開發庫時,清理資源,關閉文件等等操作,都可以放在__exit__方法當中。
Python的with語句是提供一個有效的機制,讓代碼更簡練,同時在異常產生時,清理工作更簡單。異常處理相關知識需要補充學習。

 

 

 

 

 

 

------------恢復內容結束------------


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM