首頁 >後端開發 >Python教學 >python動態捕獲異常

python動態捕獲異常

高洛峰
高洛峰原創
2016-10-18 11:01:591368瀏覽

在討論動態捕獲異常時讓我大吃一驚的是,可以讓我找到隱藏的Bug和樂趣...

有問題的代碼


下面的代碼來自一個產品中看起來是好的抽象代碼- slightly(!) .這是調用一些統計數據的函數,然後進行處理. 首先是用socket連接獲取一個值,可能發生了socket錯誤.由於統計數據在系統中不是至關重要的,我們只是記一下日誌錯誤並繼續往下走.

(請注意,這篇文章我使用doctest測試的- 這代表程式碼可以運行!)

>>> def get_stats():

...     pass

.. .

>>> def do_something_with_stats(stats):

...     pass

...

>>> try:

...   .

...     logging.warning("Can't get statistics")

... else:

...     do_something_with_stats(stats)

但其實我們注意到靜態分析報告顯示一個問題:

$ flake8 filename.py

filename.py:351:1: F821 undefined name 'socket'

filename.py:352:1: F821 definedfilename.py:352:1: F821 defined name.py:352:1: F821 definedfilename.py:352:1: F821 defined name.py:352:1: F821 defined logging'

顯然是我們沒測試,這個問題是代碼中我們沒有引用socket 和logging 兩個模組.使我感到驚奇的是,這並沒有預先拋出NameError錯,我以為它會查找這些異常語句中的一些名詞,如它需要捕捉這些異常,它需要知道些什麼呢!

事實證明並非如此,異常語句的查找是延遲完成的,只是評估時拋出異常. 不只是名稱延遲查找,也可以定制顯示聲明異常做為'參數(argument)'.

這可能是好事,壞事,或者是令人厭惡的.

這樣(上段中提到的)

異常參數可以以任意形式數值傳遞.就允許了異常的動態參數被捕獲.

>>> def do_something():

...    blob

...

>> :

...         action()

...     except ignore_spec:

...         pass

. +/of...

...

>>> attempt(do_something, ignore_spec=TypeError)

Traceback (most recent call last):

 ...

NameError: global name 'blob' is not defined

的弊端就是異常參數中的錯誤通常只有在異常觸發之後才會被注意到,不過為時已晚.當用異常去捕獲不常見的事件時(例如:以寫方式打開文件失敗),除非做個一個特定的測試用例,否則只有當一個異常(或任何異常)被觸發的時候才會知道, 屆時記錄下來並且查看是否有匹配的異常, 並且拋出它自己的錯誤異常- 這是一個NameError通常所做的事情.

>>> def do_something():

...     return 1, 2

...

>>> try:

...  

, b>> .. except ValuError:  # oops - someone can't type

...     print("Oops")

... else:

...  . do_something returns a triple...

OK!

討厭的(上段中提到的)

>>> try:

...    TypeError = ZeroDivisionrror  this nowr woo !

... except 類型...    1 / 0

... except TypeError:

...    print("Caught!")

... else:o ..

Caught!

不僅僅是異常參數通過名稱查找, - 其它的表達式也是這樣工作的:

>>> try:

...     1 / 0

... except eval(... ''.join('Zero Division Error'.split())):

...     print("Caught!")

... else:

...     print("ok")

...     print("ok")

. ..

Caught!

異常參數不僅僅只能在運行時確定,它甚至可以使用在生命週期內的異常的信息. 以下是一個比較費解的方式來捕捉拋出的異常- 但也只能如此了:

>>> import sys

>>> def current_exc_type():

...     return sys.exc_info()[0]

...

...

>

>

.     blob

... except current_exc_type():

...     print ("Got you!")

...

Got you!異常處理程序時, 我們應該首先想到的就是這種

(字節)代碼

為了確認它是如何在異常處理工作中出現的,我在一個異常的例子中運行dis.dis(). (注意這裡的分解是在Python2.7 下- 不同的字節碼是Python 3.3下產生的,但這基本上是類似的):

>>> import dis

>>> def x():

...     try:

...         pass

...     except Blobbity:

...       except Blobbity:

...      

...     else:

...         print("good")

...

>>> dis.dis(x)  CEPT             4 ( 7)

33 3pop_block

4跳躍_forward 22(至29)

4>> 7dup_top

8load_global 0(blobbity) _if_if_if_false28

1717 pop_top

18pop_top

11pop_top

520 load_const 1('bad')

23print_item

24print_newline

 7     >>   29 LOAD_CONST               2(「良好」)

            32 PRINT_ITEM

            33 PRINT_NEWLINE    

            37 RETURN_VALUE

這顯示了我最初預期的問題(問題)。 異常處理「looks」完全是依照Python運作時的內部機制。 這一步驟沒有必要知道關於後續的異常「捕獲」語句,並且如果沒有異常拋出它們將被完全忽略了。 SETUP_EXCEPT並不關心發生什麼,如果發生了異常,第一個處理程序應該被忽略評估,然後第二個,以此類推。

每個處理程序都有兩個部分組成:獲得一個異常的規則,以及剛剛發送的異常進行對比。 一切都是延遲的,一切看起來都正如與你逐行的程式碼的預期一樣,從解釋器的角度來考慮。 沒有任務聰明的事情發生了,只是突然讓它看起來非常聰明。

總結

雖然這種動態的異常參數讓我大吃一驚,但是它包含許多有趣的應用程式。 當然去實現它們的可能是個餿主意,呵呵

有時並不能總是憑直覺來確認有多少Python特性的支持- 例如在類作用域內的表達和聲明都是被顯式接受的,(而不是函數、方法、全域作用域),但並非所有的都是如此靈活的。 雖然(我認為)那將是十分美好的,但表達式被禁止認可裝飾器- 以下是Python 語法錯誤:

@(lambda fn: fn)

def x():

  pass

這是嘗試動態異常參數透過給定型別傳遞給第一個異常的例子,靜靜的忍受重複的異常:

>>> class Pushover(object):

...     exc_spec = set()

.. .

...     def attempts(self, action):

...         嘗試:

...             return turn() 行動

...             透過

...         除了BaseException 作為e:

...             self.exc_spec.add(e.__class > Pushover = Pushover()

>> >

>>> for _ in range(4):

...     try:

...         Pushover.attempt(lambda: 1 / 0)🠎 🠎 ex ( "噓」)

...    其他:

...         印刷(「耶!」)

耶!

耶!

耶!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn