您好,登錄后才能下訂單哦!
這篇“Python錯(cuò)誤異常怎么解決”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Python錯(cuò)誤異常怎么解決”文章吧。
以防萬(wàn)一你不熟悉異常,讓我們從一般定義開(kāi)始......
exception:(計(jì)算)正常處理時(shí)發(fā)生的中斷,通常由錯(cuò)誤條件引起,可由另一部分程序處理。
讓我們看一個(gè)簡(jiǎn)單的例子:
def initiate_security_protocol(code): if code == 1: print("Returning onboard companion to home location...") if code == 712: print("Dematerializing to preset location...") code = int(input("Enter security protocol code: ")) initiate_security_protocol(code)
>>> Enter security protocol code: 712 Dematerializing to preset location... >>> Enter security protocol code: seven one two Traceback (most recent call last): File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/security_protocols.py", line 7, in <module> code = int(input("Enter security protocol code: ")) ValueError: invalid literal for int() with base 10: 'seven one two'
顯然,這是一個(gè)錯(cuò)誤。我們不希望我們的程序因?yàn)橛脩?hù)輸入了一些奇怪的東西而突然崩潰。正如下面這個(gè)笑話(huà)所說(shuō)...
一名 QA 工程師走進(jìn)一家酒吧。他點(diǎn)了一杯啤酒。他點(diǎn)了五瓶啤酒。他點(diǎn)了 -1 杯啤酒。他點(diǎn)了一只蜥蜴。
我們想防止奇怪的輸入。在這種情況下,只有一個(gè)重要的故障點(diǎn):就是int()
函數(shù)。它期望接收可以轉(zhuǎn)換為整數(shù)的參數(shù),如果它沒(méi)有得到它,則會(huì)拋出ValueError
異常。為了正確處理這個(gè)問(wèn)題,我們將可能失敗的代碼包裝在一個(gè)try...except
塊中。
try: code = int(input("Enter security protocol code: ")) except ValueError: code = 0 initiate_security_protocol(code)
當(dāng)我們?cè)俅螠y(cè)試我們的代碼時(shí),我們不會(huì)遇到這種失敗錯(cuò)誤。如果我們無(wú)法從用戶(hù)那里獲得我們需要的信息,我們將只需要設(shè)置code=0
。當(dāng)然,我們可以重寫(xiě)我們的initiate_security_protocol()
函數(shù)來(lái)處理0
不同的代碼,但是我不會(huì)在這里展示,只是為了節(jié)省時(shí)間。
注意:無(wú)論出于何種原因,作為一名多語(yǔ)言程序員,我經(jīng)常忘記在 Python 中使用except
,而用大多數(shù)其他語(yǔ)言所使用的catch
語(yǔ)句。我已經(jīng)在這篇文章中打錯(cuò)了三遍(然后立即修復(fù)它)。這只是一個(gè)記憶點(diǎn)。值得慶幸的是,Python沒(méi)有catch
的關(guān)鍵字,因此語(yǔ)法錯(cuò)誤會(huì)很突出。如果你會(huì)多種語(yǔ)言,當(dāng)你感到困惑時(shí),請(qǐng)不要驚慌。python里是except
,不是catch
。
在我們深入探討該try...except
語(yǔ)句的一些更深層次的細(xì)節(jié)之前,讓我們?cè)俅位仡櫼幌略撳e(cuò)誤語(yǔ)句。畢竟,如果我們不討論錯(cuò)誤消息,那么一篇關(guān)于錯(cuò)誤處理的文章有什么用呢?在 Python 中,我們稱(chēng)之為Traceback,因?yàn)樗鼜纳婕暗牡谝恍写a到最后一行跟蹤錯(cuò)誤的起源。在許多其他語(yǔ)言中,這將被稱(chēng)為堆棧跟蹤( stack trace)。
Traceback (most recent call last): File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/security_protocols.py", line 7, in <module> code = int(input("Enter security protocol code: ")) ValueError: invalid literal for int() with base 10: 'seven one two'
我有從下到上閱讀這些信息的習(xí)慣,因?yàn)樗梢詭椭沂紫全@得最重要的信息。如果你查看最后一行,你會(huì)看到ValueError
,這是已引發(fā)的特定異常。確切的細(xì)節(jié)如下;在這種情況下,無(wú)法使用 . 將字符串'seven one two'
用int()
轉(zhuǎn)換為整數(shù)。我們還了解到它正在嘗試轉(zhuǎn)換為以10 為底的整數(shù),這在其他場(chǎng)景中可能是有用的信息。想象一下,例如,如果那行改成...
ValueError: invalid literal for int() with base 10: '5bff'
如果我們忘記指定以 16 為基數(shù)進(jìn)行int('5bff', 16)
轉(zhuǎn)化,而不是默認(rèn)值(以 10 為基數(shù)),這是完全可能的。簡(jiǎn)而言之,你應(yīng)該始終徹底閱讀并理解錯(cuò)誤消息的最后一行!有太多次我看了一半的帖子,花了半個(gè)小時(shí)追錯(cuò)了bug,才發(fā)現(xiàn)我忘記了一個(gè)參數(shù)或使用了錯(cuò)誤的函數(shù)。
錯(cuò)誤消息上方是錯(cuò)誤來(lái)自 ( code = int(input("Enter security protocol code: "))
) 的代碼行。上面是文件的絕對(duì)路徑 ( security_protocols.py
) 和行號(hào)7
。該語(yǔ)句in <module>
意味著代碼在任何函數(shù)之外。在這個(gè)例子中,回調(diào)只有一步,所以讓我們看一些稍微復(fù)雜一點(diǎn)的東西。我已經(jīng)更改并擴(kuò)展了之前的代碼。
Traceback (most recent call last): File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/databank.py", line 6, in <module> decode_message("Bad Wolf") File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/databank.py", line 4, in decode_message initiate_security_protocol(message) File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/security_protocols.py", line 2, in initiate_security_protocol code = int(code) ValueError: invalid literal for int() with base 10: 'Bad Wolf'
我們遇到了與以前類(lèi)似的錯(cuò)誤 - 我們正在嘗試將字符串轉(zhuǎn)換為整數(shù),但它不起作用。倒數(shù)第二行向我們展示了失敗的代碼;果然,這兒提到了int()
這一點(diǎn)。根據(jù)上面的行,這個(gè)有問(wèn)題的代碼位于security_protocols.py
文件的第2 行initiate_security_protocol()
函數(shù)內(nèi)部!我們就可以馬上找到那兒,并將其包裝在一個(gè)try...except
. 了解了為什么從下到上閱讀可以節(jié)省時(shí)間了吧?
然而,讓我們想象它并不那么簡(jiǎn)單。也許我們沒(méi)有修改security_protocols.py
的選項(xiàng),所以我們需要在執(zhí)行該模塊之前防止出現(xiàn)問(wèn)題。如果我們查看下一行,我們會(huì)看到在databank.py
第 4 行,在decode_message()
函數(shù)內(nèi)部,我們調(diào)用的initiate_security_protocol()
函數(shù)有問(wèn)題。是由于在databank.py
第6行被調(diào)用,這就是我們將參數(shù)傳遞"Bad Wolf"
給它的地方。
數(shù)據(jù)輸入不是問(wèn)題,因?yàn)槲覀円獯a消息“Bad Wolf”。但是,為什么我們要將我們?cè)噲D解碼的消息傳遞給安全協(xié)議呢?也許我們需要改寫(xiě)那個(gè)函數(shù)(或者除了其他更改之外?)。如你所見(jiàn),Traceback對(duì)于了解錯(cuò)誤的來(lái)源非常重要。養(yǎng)成仔細(xì)閱讀的習(xí)慣;許多有用的信息可能隱藏在意想不到的地方。
順便說(shuō)一句,第一行每次都是一樣的,但是如果你忘記如何閱讀這些消息,它會(huì)非常有用。最近執(zhí)行的代碼列在最后。因此,正如我之前所說(shuō),你應(yīng)該從下往上閱讀它們。
“請(qǐng)求寬恕比獲得許可更容易。” -海軍少將格蕾絲·霍珀
這句話(huà)最初是關(guān)于主動(dòng)的;如果你相信一個(gè)想法,請(qǐng)盡力去嘗試它,而不是等待其他人的許可來(lái)追求它。然而,在這種情況下,它很好地描述了 Python 的錯(cuò)誤處理哲學(xué):如果某些事情經(jīng)常以一種或多種特定方式失敗,通常最好使用try...except
語(yǔ)句來(lái)處理這些情況。
這種哲學(xué)被正式命名為“請(qǐng)求寬恕比許可更容易”,或EAFP。
這有點(diǎn)抽象,所以讓我們考慮另一個(gè)例子。假設(shè)我們希望能夠在字典中查找信息。
datafile_index = { # Omitted for brevity. # Just assume there's a lot of data in here. } def get_datafile_id(subject): id = datafile_index[subject] print(f"See datafile {id}.") get_datafile_id("Clara Oswald") get_datafile_id("Ashildir")
See datafile 6035215751266852927. Traceback (most recent call last): File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/databank.py", line 30, in <module> get_datafile_id("Ashildir") File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/databank.py", line 26, in get_datafile_id id = datafile_index[subject] KeyError: 'Ashildir'
第一個(gè)函數(shù)調(diào)用是對(duì)的。我們?cè)谧值?code>database_index中搜索存在的鍵"Clara Oswald"
,因此我們返回與其關(guān)聯(lián)的值 (6035215751266852927
),并在我們格式化語(yǔ)句print()
中打印出該數(shù)據(jù)。但是,第二個(gè)函數(shù)調(diào)用失敗。引發(fā)異常KeyError
,因?yàn)?code>"Ashildir"它不是字典中的鍵。
技術(shù)說(shuō)明: Python 為collections.defaultdict
這個(gè)確切問(wèn)題提供了另一種解決方案;嘗試訪(fǎng)問(wèn)不存在的鍵將使用一些默認(rèn)值在字典中創(chuàng)建鍵/值對(duì)。但是,由于這是演示錯(cuò)誤處理的示例,因此我沒(méi)有使用它。
由于不能合理地期望我們知道或記住字典中的所有鍵,尤其是在現(xiàn)實(shí)世界的場(chǎng)景中,我們需要一些方法來(lái)處理嘗試訪(fǎng)問(wèn)不存在的鍵的常見(jiàn)情況。你的第一直覺(jué)可能是在嘗試訪(fǎng)問(wèn)字典鍵之前檢查它......
def get_datafile_id(subject): if subject in datafile_index: id = datafile_index[subject] print(f"See datafile {id}.") else: print(f"Datafile not found on {subject})
在 Python 文化中,這種方法被稱(chēng)為“跳前看”[LBYL]。
但這不是最有效的方法!“寬恕,而不是許可”在這里發(fā)揮作用:我們不是先測(cè)試,而是使用try...except
.
def get_datafile_id(subject): try: id = datafile_index[subject] print(f"See datafile {id}.") except KeyError: print(f"Datafile not found on {subject}")
這背后的邏輯很簡(jiǎn)單:我們不是通過(guò)兩次獲取鍵,而是只訪(fǎng)問(wèn)一次,并使用實(shí)際exception作為邏輯分支的手段。
在 Python 中,我們不認(rèn)為異常是可以避免的。事實(shí)上,try...except
它是許多 Python 設(shè)計(jì)模式和算法的常規(guī)部分。不要害怕引發(fā)和捕獲異常!事實(shí)上,即使是鍵盤(pán)中斷也是通過(guò)KeyboardInterrupt
異常處理的。
注意: try...except
是一個(gè)強(qiáng)大的工具,但它并不適用于一切。例如,None
從函數(shù)返回通常被認(rèn)為比引發(fā)異常更好。僅在發(fā)生最好由調(diào)用者處理的實(shí)際錯(cuò)誤時(shí)拋出異常。
遲早,每個(gè) Python 開(kāi)發(fā)人員都會(huì)發(fā)現(xiàn)這是可行的:
try: someScaryFunction() except: print("An error occured. Moving on!")
一個(gè)單except
語(yǔ)句允許你在一個(gè)中捕獲所有異常。這個(gè)被稱(chēng)為反面模式,這是一個(gè)非常非常糟糕的想法??偨Y(jié)一下……
...實(shí)際錯(cuò)誤的所有上下文放在了一起拋出,永遠(yuǎn)看不到問(wèn)題跟蹤器的內(nèi)部。當(dāng)發(fā)生“大量”異常時(shí),堆棧跟蹤指向發(fā)生次要錯(cuò)誤的位置,而不是 try 塊內(nèi)的實(shí)際失敗位置。
長(zhǎng)話(huà)短說(shuō),你應(yīng)該始終明確地捕獲特定的異常類(lèi)型。任何你無(wú)法預(yù)見(jiàn)的失敗都可能與一些需要解決的錯(cuò)誤有關(guān);例如,當(dāng)你的超級(jí)復(fù)雜搜索功能突然開(kāi)始提出 anOSError
而不是預(yù)期的KeyError
或者TypeError
時(shí)。
像往常一樣,The Zen Python 對(duì)此有話(huà)要說(shuō)……
錯(cuò)誤永遠(yuǎn)不應(yīng)該悄無(wú)聲息地過(guò)去。
除非明確沉默。
換句話(huà)說(shuō),這不是口袋妖怪 - 你不應(yīng)該抓住他們!
我不會(huì)一下子就捕捉到所有異常。那么,如何處理多個(gè)可能的故障呢?
你要知道 Python 的try...except
工具比它最初展示的要多得多。
class SonicScrewdriver: def __init__(self): self.memory = 0 def perform_division(self, lhs, rhs): try: result = float(lhs)/float(rhs) except ZeroDivisionError: print("Wibbly wobbly, timey wimey.") result = "Infinity" except (ValueError, UnicodeError): print("Oy! Don't diss the sonic!") result = "Cannot Calculate" else: self.memory = result finally: print(f"Calculation Result: {result}\n") sonic = SonicScrewdriver() sonic.perform_division(8, 4) sonic.perform_division(4, 0) sonic.perform_division(4, "zero") print(f"Memory Is: {sonic.memory}")
在我向你展示輸出之前,請(qǐng)仔細(xì)查看代碼。你認(rèn)為這三個(gè)sonic.perform_division()
函數(shù)調(diào)用中的每一個(gè)都會(huì)打印出什么?最終存儲(chǔ)sonic.memory
的是什么?看看你能不能弄明白。
如果你已經(jīng)有答案?讓我們看看你是否正確。
Calculation Result: 2.0 Wibbly wobbly, timey wimey. Calculation Result: Infinity Oy! Don't diss the sonic! Calculation Result: Cannot Calculate Memory Is: 2.0
你是驚訝,還是你做對(duì)了?讓我們分析一下。
try:
當(dāng)然,是我們?cè)噲D運(yùn)行的代碼,它可能會(huì)也可能不會(huì)引發(fā)異常。
except ZeroDivisionError:
當(dāng)我們?cè)噲D除以零時(shí)發(fā)生。在這種情況下,我們說(shuō)該值"Infinity"
是計(jì)算的結(jié)果,并打印出一條關(guān)于除以0的提示信息。
except (ValueError, UnicodeError):
只要引發(fā)這兩個(gè)異常之一,就會(huì)拋出異常。ValueError
是每當(dāng)我們傳遞的任何參數(shù)都不能被強(qiáng)制轉(zhuǎn)換float()
時(shí),就會(huì)發(fā)生這種錯(cuò)誤,而UnicodeError
是如果編碼或解碼 Unicode 有問(wèn)題,就會(huì)發(fā)生這種報(bào)錯(cuò)。實(shí)際上,第二個(gè)只是為了說(shuō)明一點(diǎn)。對(duì)于ValueError
所有無(wú)法將參數(shù)轉(zhuǎn)換為浮點(diǎn)數(shù)的可信場(chǎng)景,這已經(jīng)足夠了。無(wú)論哪種情況,我們都將值"Cannot Calculate"
作為我們的結(jié)果,并提醒用戶(hù)不要對(duì)硬件提出不合理的要求。
這就是事情變得有趣的地方。僅在未引發(fā)異常時(shí)else:
運(yùn)行。在這種情況下,如果我們有一個(gè)有效的除法計(jì)算結(jié)果,我們實(shí)際上希望將它存儲(chǔ)在內(nèi)存中;相反,如果我們得到“無(wú)窮大”或“無(wú)法計(jì)算”作為我們的結(jié)果,我們不會(huì)存儲(chǔ)它。
無(wú)論如何,該finally:
部分都會(huì)運(yùn)行。在這種情況下,我們打印出我們的計(jì)算結(jié)果。
順序確實(shí)很重要。我們必須遵循模式try...except...else...finally
,else
如果存在,必須在所有except
語(yǔ)句之后。finally
總是最后的。
最初很容易混淆else
和finally
,因此請(qǐng)確保你了解其中的區(qū)別。else
僅在未引發(fā)異常時(shí)運(yùn)行;finally
每次運(yùn)行。
你希望以下代碼實(shí)現(xiàn)什么?
class SonicScrewdriver: def __init__(self): self.memory = 0 def perform_division(self, lhs, rhs): try: result = float(lhs)/float(rhs) except ZeroDivisionError: print("Wibbly wobbly, timey wimey.") result = "Infinity" except (ValueError, UnicodeError): print("Oy! Don't diss the sonic!") result = "Cannot Calculate" else: self.memory = result return result finally: print(f"Calculation Result: {result}\n") result = -1 sonic = SonicScrewdriver() print(sonic.perform_division(8, 4))
下面的那return
句話(huà)else
應(yīng)該是事情的結(jié)束了吧?其實(shí),不!如果我們運(yùn)行該代碼...
Calculation Result: 2.0 2.0
有兩個(gè)重要的觀(guān)察結(jié)果:
finally
正在運(yùn)行,即使在我們的return
聲明之后。該函數(shù)不會(huì)像通常那樣退出。
該return
語(yǔ)句確實(shí)在finally
塊執(zhí)行之前運(yùn)行。我們知道這一點(diǎn)是因?yàn)榻Y(jié)果result
輸出是2.0
,而不是我們?cè)谡Z(yǔ)句finally
中分配的-1
。
finally
每次都會(huì)運(yùn)行,即使你return
在try...except
結(jié)構(gòu)中的其他地方有。
但是,我也用一個(gè)os.abort()
代替測(cè)試了上面的return result
,在這種情況下,finally
塊永遠(yuǎn)不會(huì)運(yùn)行;該程序徹底中止。你可以在任何地方直接停止程序執(zhí)行,Python 只會(huì)放棄它正在做的事情并退出。該規(guī)則是不變的,即使是不尋常的finally
行為。
所以,我們可以用try...except
捕獲異常. 但是如果我們只是想主動(dòng)拋出一個(gè)呢?
在 Python 術(shù)語(yǔ)中,我們說(shuō)我們引發(fā)了異常,并且與該語(yǔ)言中的大多數(shù)事情一樣,實(shí)現(xiàn)這一點(diǎn)很明顯:只需使用raise
關(guān)鍵字:
class Tardis: def __init__(self): pass def camouflage(self): raise NotImplementedError('Chameleon circuits are stuck.') tardis = Tardis() tardis.camouflage()
當(dāng)我們執(zhí)行該代碼時(shí),我們會(huì)看到我們引發(fā)的異常。
Traceback (most recent call last): File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/tardis.py", line 10, in <module> tardis.camoflague() File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/tardis.py", line 7, in camoflague raise NotImplementedError('Chameleon circuits are stuck.') NotImplementedError: Chameleon circuits are stuck.
這樣就能知道我們?cè)谀膬撼霈F(xiàn)了異常錯(cuò)誤。
注意:異常NotImplementedError是Python 中的內(nèi)置異常之一,有時(shí)用于指示一個(gè)函數(shù)不應(yīng)該使用,因?yàn)樗€沒(méi)有完成(但總有一天會(huì)完成)。它不能與NotImplementedvalue互換。請(qǐng)參閱文檔以了解何時(shí)使用它們。
顯然,關(guān)鍵代碼是raise NotImplementedError('Chameleon circuits are stuck.')
. 在raise
關(guān)鍵字之后,我們給出要引發(fā)的異常對(duì)象的名稱(chēng)。在大多數(shù)情況下,我們從 Exception 類(lèi)創(chuàng)建一個(gè)新對(duì)象,從括號(hào)的使用可以看出。所有異常都接受字符串作為消息的第一個(gè)參數(shù)。一些例外接受或需要更多參數(shù),因此請(qǐng)參閱官方文檔。
有時(shí)我們需要在捕捉到異常后對(duì)其進(jìn)行處理。我們有一些非常簡(jiǎn)單的方法來(lái)做到這一點(diǎn)。
最明顯的是從異常中打印消息。為此,我們需要能夠處理我們捕獲的異常對(duì)象。讓我們將except
語(yǔ)句更改為except NotImplementedError as e:
,其中e
是我們“綁定”到異常對(duì)象的名稱(chēng)。然后,我們可以e
直接作為對(duì)象使用。
tardis = Tardis() try: tardis.camouflage() except NotImplementedError as e: print(e)
異常類(lèi)已經(jīng)定義了它的__str__()
函數(shù)來(lái)返回異常消息,所以如果我們將它轉(zhuǎn)換成一個(gè)字符串(str()
),這就是我們將得到的。你可能還記得上一篇文章print()
自動(dòng)將其參數(shù)轉(zhuǎn)換為字符串。當(dāng)我們運(yùn)行這段代碼時(shí),我們得到...
Chameleon circuits are stuck.
是不是很容易!
現(xiàn)在,如果我們想再次引發(fā)異常怎么辦?
等等,什么?我們剛剛捕獲了那個(gè)東西。為什么還要再次引發(fā)異常?
一個(gè)示例是,如果你需要在幕后進(jìn)行一些清理工作,但最終仍希望調(diào)用者必須處理異常。這是一個(gè)例子......
class Byzantium: def __init__(self): self.power = 0 def gravity_field(self): if self.power <= 0: raise SystemError("Gravity Failing") def grab_handle(): pass byzantium = Byzantium() try: byzantium.gravity_field() except SystemError: grab_handle() print("Night night") raise
在上面的示例中,我們只是想捕獲一些實(shí)體 ( grab_handle()
) 并打印一條附加消息,然后讓異常繼續(xù)raise
拋出. 當(dāng)我們重新引發(fā)異常時(shí),我們說(shuō)它冒泡了。
Night night Traceback (most recent call last): File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/byzantium.py", line 18, in <module> byzantium.gravity_field() File "/home/jason/Code/FiringRanges/PyFiringRange/Sandbox/byzantium.py", line 8, in gravity_field raise SystemError("Gravity Failing") SystemError: Gravity Failing
注意:你可能認(rèn)為我們需要說(shuō)except SystemError as e:
或者說(shuō)raise e
什么,但那是矯枉過(guò)正。對(duì)于冒泡的異常,我們只需要自己調(diào)用raise
。
現(xiàn)在,如果我們想在冒泡異常的同時(shí)添加一些額外的信息怎么辦?你的第一個(gè)猜測(cè)可能只是完全引發(fā)一個(gè)新異常,但這會(huì)帶來(lái)一些問(wèn)題。為了演示,我將在執(zhí)行順序中添加另一層。請(qǐng)注意,當(dāng)我處理這個(gè)問(wèn)題時(shí)SystemError
,我會(huì)提出一個(gè)新的RuntimeError
。我在第二個(gè)try...except
區(qū)塊中發(fā)現(xiàn)了這個(gè)新異常。
byzantium = Byzantium() def test(): try: byzantium.gravity_field() except SystemError: grab_handle() raise RuntimeError("Night night") try: test() except RuntimeError as e: print(e) print(e.__cause__)
當(dāng)我們運(yùn)行它時(shí),我們得到以下輸出。
Night night None
當(dāng)我們捕獲到這個(gè)新異常時(shí),我們完全沒(méi)有關(guān)于它是什么原因的上下文。為了解決這個(gè)問(wèn)題,Python 3在PEP 3134中引入了顯式異常鏈接。實(shí)現(xiàn)它很容易??纯次覀兊男潞瘮?shù)test()
,這是我與上一個(gè)示例相比唯一更改的部分。
byzantium = Byzantium() def test(): try: byzantium.gravity_field() except SystemError as e: grab_handle() raise RuntimeError("Night night") from e try: test() except RuntimeError as e: print(e) print(e.__cause__)
你有沒(méi)有發(fā)現(xiàn)我在那兒做什么?在except
聲明中,我將名稱(chēng)綁定e
到我們捕獲的原始異常。然后,在引發(fā)新RuntimeError
異常時(shí),我將其鏈接到上一個(gè)異常,并使用from e
. 我們現(xiàn)在的輸出...
Night night Gravity Failing
當(dāng)我們運(yùn)行它時(shí),我們的新異常會(huì)記住它是從哪里來(lái)的——前一個(gè)異常存儲(chǔ)在它的__cause__
屬性中(打印在輸出的第二行)。這對(duì)于日志記錄特別有用。
你可以使用異常類(lèi)執(zhí)行許多其他技巧,尤其是在引入 PEP 3134 時(shí)。像往常一樣,我建議你閱讀文檔,我在文章末尾鏈接到該文檔。
Python 有一大堆異常,它們的使用有據(jù)可查。當(dāng)我為工作選擇合適的異常時(shí),我經(jīng)常參考這個(gè)異常列表。然而,有時(shí),我們只需要更多……定制的東西。
所有錯(cuò)誤類(lèi)型的異常都是從Exception
類(lèi)派生的,而類(lèi)又是從BaseException
類(lèi)派生的。這種雙重層次結(jié)構(gòu)的原因是你可以捕獲所有錯(cuò)誤Exceptions
,而無(wú)需對(duì)特殊的、非系統(tǒng)退出的異常(如KeyboardInterrupt
. 當(dāng)然,這在實(shí)踐中對(duì)你來(lái)說(shuō)并不重要,因?yàn)?code>except Exception實(shí)際上總是我之前提到的反模式的另一種形式。無(wú)論如何,不建議你直接派生自BaseException
——只要知道它存在即可。
在進(jìn)行自定義異常時(shí),你實(shí)際上可以從任何你喜歡的異常類(lèi)派生。有時(shí),最好從與你正在自定義的異常最接近的異常中獲取。但是,如果你不知所措,你可以從Exception
派生.
讓我們自定義一個(gè),好嗎?
class SpacetimeError(Exception): def __init__(self, message): super().__init__(message) class Tardis(): def __init__(self): self._destination = "" self._timestream = [] def cloister_bell(self): print("(Ominous bell tolling)") def dematerialize(self): self._timestream.append(self._destination) print("(Nifty whirring sound)") def set_destination(self, dest): if dest in self._timestream: self.cloister_bell() self._destination = dest def engage(self): if self._destination in self._timestream: raise SpacetimeError("You should not cross your own timestream!") else: self.dematerialize() tardis = Tardis() # Should be fine tardis.set_destination("7775/349x10,012/acorn") tardis.engage() # Also fine tardis.set_destination("5136/161x298,58/delta") tardis.engage() # The TARDIS is not going to like this... tardis.set_destination("7775/349x10,012/acorn") tardis.engage()
顯然,最后一個(gè)將導(dǎo)致我們的SpacetimeError
異常被引發(fā)。
讓我們?cè)倏纯茨莻€(gè)異常類(lèi)聲明。
class SpacetimeError(Exception): def __init__(self, message): super().__init__(message)
這實(shí)際上非常容易編寫(xiě)。如果你還記得我們之前對(duì)類(lèi)的探索,super().__init__()
就是在基類(lèi)上調(diào)用初始化函數(shù),Exception
在這種情況下就是這樣。我們將消息傳遞給SpacetimeError
異常構(gòu)造函數(shù),并將其交給基類(lèi)初始化函數(shù)。
事實(shí)上,如果我唯一要做的就是將 傳遞message
給super()
, 類(lèi),我可以讓這更簡(jiǎn)單:
class SpacetimeError(Exception): pass
Python 自己處理基礎(chǔ)異常。
這就是我們需要做的所有事情,盡管像往常一樣,我們可以用這個(gè)做更多的技巧。自定義異常不僅僅是一個(gè)漂亮的名字;我們可以使用它們來(lái)處理各種不尋常的錯(cuò)誤場(chǎng)景,盡管這顯然超出了本指南的范圍。
以上就是關(guān)于“Python錯(cuò)誤異常怎么解決”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。