溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Python錯(cuò)誤異常怎么解決

發(fā)布時(shí)間:2022-10-09 10:00:34 來(lái)源:億速云 閱讀:169 作者:iii 欄目:web開(kāi)發(fā)

這篇“Python錯(cuò)誤異常怎么解決”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Python錯(cuò)誤異常怎么解決”文章吧。

開(kāi)始捕捉

以防萬(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。

閱讀Traceback

在我們深入探討該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)該從下往上閱讀它們。

Exception

“請(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)該抓住他們!

Except, Else, Finally

我不會(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總是最后的。

最初很容易混淆elsefinally,因此請(qǐng)確保你了解其中的區(qū)別。else僅在未引發(fā)異常時(shí)運(yùn)行;finally每次運(yùn)行。

Finally執(zhí)行順序

你希望以下代碼實(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é)果:

  1. finally正在運(yùn)行,即使在我們的return聲明之后。該函數(shù)不會(huì)像通常那樣退出。

  2. 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)行,即使你returntry...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í)上,如果我唯一要做的就是將 傳遞messagesuper(), 類(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è)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI