天天看點

Python 中 with...as的用法

with…as,就是個python控制流語句,像 if ,while一樣。

with…as語句是簡化版的try except finally語句。

那我們先了解一下try…except…finally語句是幹啥的。實際上,try…except語句和try…finally語句是兩種語句,用于不同的場景。但是當二者結合在一起時,可以“實作穩定性和靈活性更好的設計”。

用于處理程式執行過程中的異常情況,比如文法錯誤、從未定義變量上取值等等,也就是一些python程式本身引發的異常、報錯。比如你在python下面輸入 1 / 0:

系統會給你一個ZeroDivisionError的報錯。說白了就是為了防止一些報錯影響你的程式繼續運作,就用try語句把它們抓出來(捕獲)。

try…except的标準格式:

程式執行流程是:

Tips: 我們發現,一旦跳入了某條except語句,就會執行相應的異常處理方法(block),執行完畢就會結束。不會再傳回try的normal block繼續執行了。

輸出:

結果是,先打出了一個0,又打出了一個Error。就是把ZeroDivisionError錯誤捕獲了。

先執行try後面這一堆語句,由上至下:

step1: a 正常,列印a. 于是列印出0.5 (python3.x以後都輸出浮點數)

step2: b, 不正常了,0 不能做除數,是以這是一個錯誤。直接跳到except報錯去。于是列印了Error。

step3: 其實沒有step3,因為程式結束了。c是在錯誤發生之後的b語句後才出現,根本輪不到執行它。也就看不到列印出的c了

但這還不是try/except的所有用法

except後面還能跟表達式的! 所謂的表達式,就是錯誤的定義。也就是說,我們可以捕捉一些我們想要捕捉的異常。而不是什麼異常都報出來。

異常分為兩類:

python标準異常

自定義異常

我們先抛開自定義異常(因為涉及到類的概念),看看except都能捕捉到哪些python标準異常。

當程式執行到​<code>​print(m)​</code>​的時候 發現了一個​<code>​NameError: name 'm' is not defined​</code>​,于是控制流去尋找比對的except異常處理語句。發現了第一條比對,執行對應block。執行完結束。

用于無論執行過程中有沒有異常,都要執行清場工作。

好的 現在我們看看他倆合在一起怎麼用!!

tips: 注意順序不能亂,否則會有文法錯誤。如果用else就必須有except,否則會有文法錯誤。

try語句終于搞清楚了! 那麼可以繼續with…as的探險了

with as 語句的結構如下:

看這個結構我們可以擷取至少兩點資訊 1. as可以省略 2. 有一個句塊要執行。也就是說with是一個控制流語句,跟if/for/while/try之類的是一類的,with可以用來簡化try finally代碼,看起來可以比try finally更清晰。這裡新引入了一個"上下文管理協定"context management protocol,實作方法是為一個類定義__enter__和__exit__兩個函數。with expresion as variable的執行過程是,首先執行__enter__函數,它的傳回值會賦給as後面的variable,想讓它傳回什麼就傳回什麼,隻要你知道怎麼處理就可以了,如果不寫as variable,傳回值會被忽略。

然後,開始執行with-block中的語句,不論成功失敗(比如發生異常、錯誤,設定sys.exit()),在with-block執行完成後,會執行__exit__函數。這樣的過程其實等價于:

隻不過,現在把一部分代碼封裝成了__enter__函數,清理代碼封裝成__exit__函數。

所謂上下文管理協定,其實是指with後面跟的expression。這個expression一般都是一個類的實體。這個類的實體裡面要包含有對__enter__和__exit__函數的定義才行。

除了給類定義一些屬性之外,還可以定義類的方法。也就是允許對類的内容有哪些操作,最直覺的方法就是用dir()函數來看一個類的屬性和方法。比如要檢視字元串類有哪些屬性和方法:

類,除了python内置的,當然還可以自己定義! 是以除了expression表示一些含有這兩種方法的内置類,還可以自己定義類,讓他們含有enter和exit方法。

可能大家一直奇怪,為什麼有的方法,比如上圖中的'split' 'title'什麼的都沒有下劃線,而又有很多,,比如__enter__和__exit__要寫下劃線呢?請參考博文http://blog.csdn.net/qiqicos/article/details/79208039

那麼​<code>​__enter__​</code>​和​<code>​__exit__​</code>​是怎麼用的方法呢?我們直接來看一個例子好了。

輸出結果:

步驟分析:

–&gt; 調用get_sample()函數,傳回Sample類的執行個體;

–&gt; 執行Sample類中的__enter__()方法,列印"In__enter_()"字元串,并将字元串“Foo”指派給as後面的sample變量;

–&gt; 執行with-block碼塊,即列印"sample: %s"字元串,結果為"sample: Foo"

–&gt; 執行with-block碼塊結束,傳回Sample類,執行類方法__exit__()。因為在執行with-block碼塊時并沒有錯誤傳回,是以type,value,trace這三個arguments都沒有值。直接列印"In__exit__()"

程式有錯的例子:

–&gt; 執行個體化Sample類,執行類方法__enter__(),傳回值self也就是執行個體自己指派給sample。即sample是Sample的一個執行個體(對象);

–&gt;執行with-block碼塊: 執行個體sample調用方法do_something();

–&gt;執行do_something()第一行 bar = 1 / 0,發現ZeroDivisionError,直接結束with-block代碼塊運作

–&gt;執行類方法__exit__(),帶入ZeroDivisionError的錯誤資訊值,也就是type,value, trace,并列印它們。