Python異常對代碼運(yùn)行性能的影響實(shí)例解析
前言
Python的異常處理能力非常強(qiáng)大,但是用不好也會(huì)帶來負(fù)面的影響。我平時(shí)寫程序的過程中也喜歡使用異常,雖然采取防御性的方式編碼會(huì)更好,但是交給異常處理會(huì)起到偷懶作用。偶爾會(huì)想想異常處理會(huì)對性能造成多大的影響,于是今天就試著測試了一下。
Python異常(谷歌開源風(fēng)格指南)
tip:
允許使用異常, 但必須小心。
定義:
異常是一種跳出代碼塊的正??刂屏鱽硖幚礤e(cuò)誤或者其它異常條件的方式。
優(yōu)點(diǎn):
正常操作代碼的控制流不會(huì)和錯(cuò)誤處理代碼混在一起. 當(dāng)某種條件發(fā)生時(shí), 它也允許控制流跳過多個(gè)框架. 例如, 一步跳出N個(gè)嵌套的函數(shù), 而不必繼續(xù)執(zhí)行錯(cuò)誤的代碼。
缺點(diǎn):
可能會(huì)導(dǎo)致讓人困惑的控制流. 調(diào)用庫時(shí)容易錯(cuò)過錯(cuò)誤情況。
結(jié)論:
異常必須遵守特定條件:
像這樣觸發(fā)異常: raise MyException("Error message")
或者 raise MyException
. 不要使用兩個(gè)參數(shù)的形式( raise MyException, "Error message"
)或者過時(shí)的字符串異常( raise "Error message"
)。
模塊或包應(yīng)該定義自己的特定域的異?;? 這個(gè)基類應(yīng)該從內(nèi)建的Exception類繼承. 模塊的異常基類應(yīng)該叫做”Error”。
class Error(Exception): pass
永遠(yuǎn)不要使用 except
: 語句來捕獲所有異常, 也不要捕獲 Exception
或者 StandardError
, 除非你打算重新觸發(fā)該異常, 或者你已經(jīng)在當(dāng)前線程的最外層(記得還是要打印一條錯(cuò)誤消息). 在異常這方面, Python非常寬容, except
: 真的會(huì)捕獲包括Python語法錯(cuò)誤在內(nèi)的任何錯(cuò)誤. 使用 except: 很容易隱藏真正的bug。
盡量減少try/except塊中的代碼量. try塊的體積越大, 期望之外的異常就越容易被觸發(fā). 這種情況下, try/except塊將隱藏真正的錯(cuò)誤。
使用finally子句來執(zhí)行那些無論try塊中有沒有異常都應(yīng)該被執(zhí)行的代碼. 這對于清理資源常常很有用, 例如關(guān)閉文件。
當(dāng)捕獲異常時(shí), 使用 as 而不要用逗號. 例如
try: raise Error except Error as error: pass
設(shè)計(jì)實(shí)驗(yàn)方式
采取比較簡單直觀的對照實(shí)驗(yàn)。
先定義一個(gè)裝飾器,用來計(jì)算每個(gè)函數(shù)執(zhí)行所需時(shí)間:
def timer(func): import time def wrapper(*args, **kwargs): startTime = time.time() f = func(*args, **kwargs) endTime = time.time() passTime = endTime - startTime print "執(zhí)行函數(shù)%s使用了%f秒" % (getattr(func, "__name__"), passTime) return f return wrapper
然后用該裝飾器裝飾測試的函數(shù)即可。
再定義一個(gè)叫do_something的函數(shù),這個(gè)函數(shù)中就做一件事,把1賦值給變量a。在每個(gè)測試函數(shù)中,都會(huì)調(diào)用這個(gè)函數(shù)1000000次。
do_something:
def do_something(): a = 1
我根據(jù)情況設(shè)計(jì)了不同的測試組:
測試組1(直接執(zhí)行耗時(shí)操作):
@timer def test1(): for _ in xrange(1000000): do_something()
測試組2(耗時(shí)操作放在try中執(zhí)行,不拋出錯(cuò)誤):
@timer def test2(): try: for _ in xrange(1000000): do_something() except Exception: do_something() else: pass finally: pass
測試組3(try放耗時(shí)操作中,try每一次操作,不拋出錯(cuò)誤):
@timer def test3(): for _ in xrange(1000000): try: do_something() except Exception: do_something() else: pass finally: pass
測試組4(try放耗時(shí)操作中,try每一次操作并進(jìn)行異常處理(捕捉拋出的特定異常)):
@timer def test4(): zero = 0 for _ in xrange(1000000): try: if zero == 0: raise ZeroDivisionError except ZeroDivisionError: do_something() else: pass finally: pass
測試組5(try放耗時(shí)操作中,try每一次操作并進(jìn)行異常處理(捕捉所有異常 try…except BaseException)):
@timer def test5(): zero = 0 for _ in xrange(1000000): try: if zero == 0: raise ZeroDivisionError except BaseException: do_something() else: pass finally: pass
測試組6(try放耗時(shí)操作中,try每一次操作并進(jìn)行異常處理(捕捉所有異常 不帶任何異常類型)):
@timer def test6(): zero = 0 for _ in xrange(1000000): try: if zero == 0: raise ZeroDivisionError except: do_something() else: pass finally: pass
測試組7(耗時(shí)操作放在except中):
@timer def test7(): zero = 0 try: if zero == 0: raise ZeroDivisionError except ZeroDivisionError: for _ in xrange(1000000): do_something() else: pass finally: pass
測試組8(防御式編碼):
@timer def test8(): zero = 0 for _ in xrange(1000000): if zero == 0: do_something()
執(zhí)行結(jié)果
對比結(jié)論
- 通過對比1和2,可以得知直接執(zhí)行耗時(shí)操作和耗時(shí)操作放在try中執(zhí)行并無異常觸發(fā)時(shí)性能消耗幾乎是一樣的。
- 通過對比2和7,可以得知使用異常的使用無論是把代碼放在 try 中執(zhí)行還是在 except 中執(zhí)行性能消耗幾乎是一樣的。
- 通過對比2和3,可以得知當(dāng)不拋出錯(cuò)誤時(shí),把try放耗時(shí)操作中比耗時(shí)操作放在try中性能消耗要略大。
- 通過對比3和4,可以得知當(dāng)使用try時(shí)無異常拋出跟使用try時(shí)拋出異常性能消耗幾乎相差好幾倍。
- 通過對比4和5,可以得知try放耗時(shí)操作中時(shí),try每一次操作并進(jìn)行異常處理(捕捉拋出的特定異常)跟try每一次操作并進(jìn)行異常處理(捕捉所有異常 try…except BaseException)性能消耗幾乎是一樣的。
- 通過對比4和8,可以得知使用防御性方式編碼比捕捉異常方式性能消耗幾乎相差好幾倍。
- 通過對比5和6,可以得知捕捉所有異常(try…except)方式比捕捉所有異常(try…except BaseException)方式要略快。
總結(jié)
- 由以上對比結(jié)論,可以總結(jié)為:
- 無論是把代碼放在 try 中執(zhí)行還是在 except 中執(zhí)行性能消耗幾乎是一樣的。
- 直接執(zhí)行代碼與放在try中執(zhí)行且不拋出異常時(shí)性能消耗幾乎是一樣的,當(dāng)然理論上try會(huì)消耗一點(diǎn)性能,可以忽略不計(jì)。
- 雖然try…except的方式比try…except BaseException和捕捉拋出的特定異常的方式要略快,但扔不建議采取這種方式,因?yàn)榍罢吆苋菀纂[藏真正的bug,從而帶來嚴(yán)重后果。
- 通常要采取捕捉拋出的特定異常而不是捕捉所有異常,雖然二者性能消耗幾乎一樣。
- 防御性方式編碼比捕捉異常方式性能消耗幾乎相差好幾倍,應(yīng)盡量采取這種編程方式,提升性能并且更靠譜。
以上就是本文關(guān)于Python異常對代碼運(yùn)行性能的影響實(shí)例解析的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
相關(guān)文章
opencv中cv2.minAreaRect函數(shù)輸出角度問題詳解
minAreaRect返回的數(shù)據(jù)包括了矩形的中心點(diǎn),寬、高,和旋轉(zhuǎn)角度,下面這篇文章主要給大家介紹了關(guān)于opencv中cv2.minAreaRect函數(shù)輸出角度問題的相關(guān)資料,需要的朋友可以參考下2022-11-11Python?flask?框架使用flask-login?模塊的詳細(xì)過程
Flask-Login?是一個(gè)?Flask?模塊,可以為?Flask?應(yīng)用程序提供用戶登錄功能,這篇文章主要介紹了Python?flask?框架使用?flask-login?模塊,需要的朋友可以參考下2023-01-01這三個(gè)好用的python函數(shù)你不能不知道!
作為21世紀(jì)最流行的語言之一,Python當(dāng)然有很多有趣的功能值得深入探索和研究.今天通過理論和實(shí)際例子來討論,需要的朋友可以參考下2021-06-06使用matplotlib創(chuàng)建Gif動(dòng)圖的實(shí)現(xiàn)
本文主要介紹了使用matplotlib創(chuàng)建Gif動(dòng)圖的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Python簡單實(shí)現(xiàn)gif動(dòng)圖倒放示例
這篇文章主要為大家介紹了Python簡單實(shí)現(xiàn)gif動(dòng)圖倒放的示例過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05keras.layers.Layer中無法定義name的問題及解決
這篇文章主要介紹了keras.layers.Layer中無法定義name的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02