Tuesday, November 20, 2012

本文來源
http://caterpillar.onlyfun.net/Gossip/Python/WithAs.html
作者

From Gossip@caterpillar

Python Gossip: 使用 with as




在 try、raise 陳述句 中有個讀取檔案的範例:
 file = open('demo.py', 'r', encoding='UTF-8')
try:
    for line in file:
        print(line, end='')
except:
    print('讀取檔案發生錯誤')
finally:
    file.close()

為了要處理檔案讀取過程中發生的例外,並且最後確定檔案一定會關閉,你使用了try..except...finally語句,實際上,在Python 3(或2.6)中,你可以使用with as語句來簡化程式的撰寫:
 with open('demo.py', 'r', encoding='UTF-8') as file:
    for line in file:
        print(line, end='')

with之後的運算式傳回的物件,可以使用as指定給變數來參考,在上面的例子中,file所參考到的物件,最後會被自動關閉,即使在with as的區塊中發生了例外,最後一定會關閉file所參考的物件。

實際上,只要物件支援環境管理協定(Context Management Protocol),就可以使用with as語句。支援環境管理協定的物件,必須實作__enter__()與__exit__()兩個方法,這樣的物件稱之為環境管理員(Context Manager)。

with陳述句一開始執行,就會進行__enter__()方法,該方法傳回的物件,可以使用as指定給變數(如果有的話),接著就執行with區塊中的程式碼。

如果with區塊中的程式碼發生了例外,則會執行__exit__()方法,並傳入三個引數,這三個引數,與 再看 try、raise 中所提到的 sys.exc_info() 傳回的三個值是相同的,也就是例外的類型、例 外訊息以及traceback物件。此時__exit__()方法若傳回False,則例外會被重新丟出,否則例外就停止傳播,通常__exit__()會傳回False以在with之外還可以處理例外。

如果with區塊中沒有發生例外而執行完畢,則也是執行__exit__()方法,此時__exit__()的三個參數都接收到None。

所以,假設你要自行實作一個可以自動關閉檔案的物件,則可以如下:
 class FileReader:
    def __init__(self, filename):
        self.filename = filename
   
    def __enter__(self):
        self.file = open(self.filename, 'r', encoding='UTF-8')
        return self.file
   
    def __exit__(self, type, msg, traceback):
        if type:
            print(msg)       # 作你的例外處理
        self.file.close()
        return False

接下來你就可以在FileReader物件上使用with as語句。例如:
with FileReader('demo.py') as file:
    for line in file:
        print(line, end='')

No comments: