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>是怎麼用的方法呢?我們直接來看一個例子好了。
輸出結果:
步驟分析:
–> 調用get_sample()函數,傳回Sample類的執行個體;
–> 執行Sample類中的__enter__()方法,列印"In__enter_()"字元串,并将字元串“Foo”指派給as後面的sample變量;
–> 執行with-block碼塊,即列印"sample: %s"字元串,結果為"sample: Foo"
–> 執行with-block碼塊結束,傳回Sample類,執行類方法__exit__()。因為在執行with-block碼塊時并沒有錯誤傳回,是以type,value,trace這三個arguments都沒有值。直接列印"In__exit__()"
程式有錯的例子:
–> 執行個體化Sample類,執行類方法__enter__(),傳回值self也就是執行個體自己指派給sample。即sample是Sample的一個執行個體(對象);
–>執行with-block碼塊: 執行個體sample調用方法do_something();
–>執行do_something()第一行 bar = 1 / 0,發現ZeroDivisionError,直接結束with-block代碼塊運作
–>執行類方法__exit__(),帶入ZeroDivisionError的錯誤資訊值,也就是type,value, trace,并列印它們。