中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python錯誤異常怎么解決

發布時間:2022-10-09 10:00:34 來源:億速云 閱讀:171 作者:iii 欄目:web開發

這篇“Python錯誤異常怎么解決”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Python錯誤異常怎么解決”文章吧。

開始捕捉

以防萬一你不熟悉異常,讓我們從一般定義開始......

exception:(計算)正常處理時發生的中斷,通常由錯誤條件引起,可由另一部分程序處理。

讓我們看一個簡單的例子:

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'

顯然,這是一個錯誤。我們不希望我們的程序因為用戶輸入了一些奇怪的東西而突然崩潰。正如下面這個笑話所說...

一名 QA 工程師走進一家酒吧。他點了一杯啤酒。他點了五瓶啤酒。他點了 -1 杯啤酒。他點了一只蜥蜴。

我們想防止奇怪的輸入。在這種情況下,只有一個重要的故障點:就是int()函數。它期望接收可以轉換為整數的參數,如果它沒有得到它,則會拋出ValueError異常。為了正確處理這個問題,我們將可能失敗的代碼包裝在一個try...except塊中。

try:
    code = int(input("Enter security protocol code: "))
except ValueError:
    code = 0
initiate_security_protocol(code)

當我們再次測試我們的代碼時,我們不會遇到這種失敗錯誤。如果我們無法從用戶那里獲得我們需要的信息,我們將只需要設置code=0。當然,我們可以重寫我們的initiate_security_protocol()函數來處理0不同的代碼,但是我不會在這里展示,只是為了節省時間。

注意:無論出于何種原因,作為一名多語言程序員,我經常忘記在 Python 中使用except,而用大多數其他語言所使用的catch語句。我已經在這篇文章中打錯了三遍(然后立即修復它)。這只是一個記憶點。值得慶幸的是,Python沒有catch的關鍵字,因此語法錯誤會很突出。如果你會多種語言,當你感到困惑時,請不要驚慌。python里是except,不是catch

閱讀Traceback

在我們深入探討該try...except語句的一些更深層次的細節之前,讓我們再次回顧一下該錯誤語句。畢竟,如果我們不討論錯誤消息,那么一篇關于錯誤處理的文章有什么用呢?在 Python 中,我們稱之為Traceback,因為它從涉及的第一行代碼到最后一行跟蹤錯誤的起源。在許多其他語言中,這將被稱為堆棧跟蹤( 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'

我有從下到上閱讀這些信息的習慣,因為它可以幫助我首先獲得最重要的信息。如果你查看最后一行,你會看到ValueError,這是已引發的特定異常。確切的細節如下;在這種情況下,無法使用 . 將字符串'seven one two'int()轉換為整數。我們還了解到它正在嘗試轉換為以10 為底的整數,這在其他場景中可能是有用的信息。想象一下,例如,如果那行改成...

ValueError: invalid literal for int() with base 10: '5bff'

如果我們忘記指定以 16 為基數進行int('5bff', 16)轉化,而不是默認值(以 10 為基數),這是完全可能的。簡而言之,你應該始終徹底閱讀并理解錯誤消息的最后一行!有太多次我看了一半的帖子,花了半個小時追錯了bug,才發現我忘記了一個參數或使用了錯誤的函數。

錯誤消息上方是錯誤來自 ( code = int(input("Enter security protocol code: "))) 的代碼行。上面是文件的絕對路徑 ( security_protocols.py) 和行號7。該語句in <module>意味著代碼在任何函數之外。在這個例子中,回調只有一步,所以讓我們看一些稍微復雜一點的東西。我已經更改并擴展了之前的代碼。

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'

我們遇到了與以前類似的錯誤 - 我們正在嘗試將字符串轉換為整數,但它不起作用。倒數第二行向我們展示了失敗的代碼;果然,這兒提到了int()這一點。根據上面的行,這個有問題的代碼位于security_protocols.py文件的第2 行initiate_security_protocol()函數內部!我們就可以馬上找到那兒,并將其包裝在一個try...except. 了解了為什么從下到上閱讀可以節省時間了吧?

然而,讓我們想象它并不那么簡單。也許我們沒有修改security_protocols.py的選項,所以我們需要在執行該模塊之前防止出現問題。如果我們查看下一行,我們會看到在databank.py第 4 行,在decode_message()函數內部,我們調用的initiate_security_protocol()函數有問題。是由于在databank.py第6行被調用,這就是我們將參數傳遞"Bad Wolf"給它的地方。

數據輸入不是問題,因為我們要解碼消息“Bad Wolf”。但是,為什么我們要將我們試圖解碼的消息傳遞給安全協議呢?也許我們需要改寫那個函數(或者除了其他更改之外?)。如你所見,Traceback對于了解錯誤的來源非常重要。養成仔細閱讀的習慣;許多有用的信息可能隱藏在意想不到的地方。

順便說一句,第一行每次都是一樣的,但是如果你忘記如何閱讀這些消息,它會非常有用。最近執行的代碼列在最后。因此,正如我之前所說,你應該從下往上閱讀它們。

Exception

“請求寬恕比獲得許可更容易。” -海軍少將格蕾絲·霍珀

這句話最初是關于主動的;如果你相信一個想法,請盡力去嘗試它,而不是等待其他人的許可來追求它。然而,在這種情況下,它很好地描述了 Python 的錯誤處理哲學:如果某些事情經常以一種或多種特定方式失敗,通常最好使用try...except語句來處理這些情況。

這種哲學被正式命名為“請求寬恕比許可更容易”,或EAFP

這有點抽象,所以讓我們考慮另一個例子。假設我們希望能夠在字典中查找信息。

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'

第一個函數調用是對的。我們在字典database_index中搜索存在的鍵"Clara Oswald",因此我們返回與其關聯的值 (6035215751266852927 ),并在我們格式化語句print()中打印出該數據。但是,第二個函數調用失敗。引發異常KeyError,因為"Ashildir"它不是字典中的鍵。

技術說明: Python 為collections.defaultdict這個確切問題提供了另一種解決方案;嘗試訪問不存在的鍵將使用一些默認值在字典中創建鍵/值對。但是,由于這是演示錯誤處理的示例,因此我沒有使用它。

由于不能合理地期望我們知道或記住字典中的所有鍵,尤其是在現實世界的場景中,我們需要一些方法來處理嘗試訪問不存在的鍵的常見情況。你的第一直覺可能是在嘗試訪問字典鍵之前檢查它......

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 文化中,這種方法被稱為“跳前看”[LBYL]。

但這不是最有效的方法!“寬恕,而不是許可”在這里發揮作用:我們不是先測試,而是使用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}")

這背后的邏輯很簡單:我們不是通過兩次獲取鍵,而是只訪問一次,并使用實際exception作為邏輯分支的手段。

在 Python 中,我們不認為異常是可以避免的。事實上,try...except它是許多 Python 設計模式和算法的常規部分。不要害怕引發和捕獲異常!事實上,即使是鍵盤中斷也是通過KeyboardInterrupt異常處理的。

注意: try...except是一個強大的工具,但它并不適用于一切。例如,None從函數返回通常被認為比引發異常更好。僅在發生最好由調用者處理的實際錯誤時拋出異常。

反面模式

遲早,每個 Python 開發人員都會發現這是可行的:

try:
    someScaryFunction()
except:
    print("An error occured. Moving on!")

一個單except語句允許你在一個中捕獲所有異常。這個被稱為反面模式,這是一個非常非常糟糕的想法。總結一下……

...實際錯誤的所有上下文放在了一起拋出,永遠看不到問題跟蹤器的內部。當發生“大量”異常時,堆棧跟蹤指向發生次要錯誤的位置,而不是 try 塊內的實際失敗位置。

長話短說,你應該始終明確地捕獲特定的異常類型。任何你無法預見的失敗都可能與一些需要解決的錯誤有關;例如,當你的超級復雜搜索功能突然開始提出 anOSError而不是預期的KeyError或者TypeError時。

像往常一樣,The Zen Python 對此有話要說……

錯誤永遠不應該悄無聲息地過去。
除非明確沉默。

換句話說,這不是口袋妖怪 - 你不應該抓住他們!

Except, Else, Finally

我不會一下子就捕捉到所有異常。那么,如何處理多個可能的故障呢?

你要知道 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}")

在我向你展示輸出之前,請仔細查看代碼。你認為這三個sonic.perform_division()函數調用中的每一個都會打印出什么?最終存儲sonic.memory的是什么?看看你能不能弄明白。

如果你已經有答案?讓我們看看你是否正確。

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

你是驚訝,還是你做對了?讓我們分析一下。

try:當然,是我們試圖運行的代碼,它可能會也可能不會引發異常。

except ZeroDivisionError:當我們試圖除以零時發生。在這種情況下,我們說該值"Infinity"是計算的結果,并打印出一條關于除以0的提示信息。

except (ValueError, UnicodeError):只要引發這兩個異常之一,就會拋出異常。ValueError是每當我們傳遞的任何參數都不能被強制轉換float()時,就會發生這種錯誤,而UnicodeError是如果編碼或解碼 Unicode 有問題,就會發生這種報錯。實際上,第二個只是為了說明一點。對于ValueError所有無法將參數轉換為浮點數的可信場景,這已經足夠了。無論哪種情況,我們都將值"Cannot Calculate"作為我們的結果,并提醒用戶不要對硬件提出不合理的要求。

這就是事情變得有趣的地方。僅在未引發異常時else:運行。在這種情況下,如果我們有一個有效的除法計算結果,我們實際上希望將它存儲在內存中;相反,如果我們得到“無窮大”或“無法計算”作為我們的結果,我們不會存儲它。

無論如何,該finally:部分都會運行。在這種情況下,我們打印出我們的計算結果。

順序確實很重要。我們必須遵循模式try...except...else...finallyelse如果存在,必須在所有except語句之后。finally總是最后的。

最初很容易混淆elsefinally,因此請確保你了解其中的區別。else僅在未引發異常時運行;finally每次運行。

Finally執行順序

你希望以下代碼實現什么?

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句話else應該是事情的結束了吧?其實,不!如果我們運行該代碼...

Calculation Result: 2.0

2.0

有兩個重要的觀察結果:

  1. finally正在運行,即使在我們的return聲明之后。該函數不會像通常那樣退出。

  2. return語句確實在finally塊執行之前運行。我們知道這一點是因為結果result輸出是2.0,而不是我們在語句finally中分配的-1

finally每次都會運行,即使你returntry...except結構中的其他地方有。

但是,我也用一個os.abort()代替測試了上面的return result,在這種情況下,finally塊永遠不會運行;該程序徹底中止。你可以在任何地方直接停止程序執行,Python 只會放棄它正在做的事情并退出。該規則是不變的,即使是不尋常的finally行為。

拋出異常

所以,我們可以用try...except捕獲異常. 但是如果我們只是想主動拋出一個呢?

在 Python 術語中,我們說我們引發了異常,并且與該語言中的大多數事情一樣,實現這一點很明顯:只需使用raise關鍵字:

class Tardis:

    def __init__(self):
        pass

    def camouflage(self):
        raise NotImplementedError('Chameleon circuits are stuck.')

tardis = Tardis()
tardis.camouflage()

當我們執行該代碼時,我們會看到我們引發的異常。

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.

這樣就能知道我們在哪兒出現了異常錯誤。

注意:異常NotImplementedError是Python 中的內置異常之一,有時用于指示一個函數不應該使用,因為它還沒有完成(但總有一天會完成)。它不能與NotImplementedvalue互換。請參閱文檔以了解何時使用它們。

顯然,關鍵代碼是raise NotImplementedError('Chameleon circuits are stuck.'). 在raise關鍵字之后,我們給出要引發的異常對象的名稱。在大多數情況下,我們從 Exception 類創建一個新對象,從括號的使用可以看出。所有異常都接受字符串作為消息的第一個參數。一些例外接受或需要更多參數,因此請參閱官方文檔。

使用異常

有時我們需要在捕捉到異常后對其進行處理。我們有一些非常簡單的方法來做到這一點。

最明顯的是從異常中打印消息。為此,我們需要能夠處理我們捕獲的異常對象。讓我們將except語句更改為except NotImplementedError as e:,其中e是我們“綁定”到異常對象的名稱。然后,我們可以e直接作為對象使用。

tardis = Tardis()

try:
    tardis.camouflage()
except NotImplementedError as e:
    print(e)

異常類已經定義了它的__str__()函數來返回異常消息,所以如果我們將它轉換成一個字符串(str()),這就是我們將得到的。你可能還記得上一篇文章print()自動將其參數轉換為字符串。當我們運行這段代碼時,我們得到...

Chameleon circuits are stuck.

是不是很容易!

冒泡

現在,如果我們想再次引發異常怎么辦?

等等,什么?我們剛剛捕獲了那個東西。為什么還要再次引發異常?

一個示例是,如果你需要在幕后進行一些清理工作,但最終仍希望調用者必須處理異常。這是一個例子......

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

在上面的示例中,我們只是想捕獲一些實體 ( grab_handle()) 并打印一條附加消息,然后讓異常繼續raise拋出. 當我們重新引發異常時,我們說它冒泡了

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

注意:你可能認為我們需要說except SystemError as e:或者說raise e什么,但那是矯枉過正。對于冒泡的異常,我們需要自己調用raise

現在,如果我們想在冒泡異常的同時添加一些額外的信息怎么辦?你的第一個猜測可能只是完全引發一個新異常,但這會帶來一些問題。為了演示,我將在執行順序中添加另一層。請注意,當我處理這個問題時SystemError,我會提出一個新的RuntimeError。我在第二個try...except區塊中發現了這個新異常。

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__)

當我們運行它時,我們得到以下輸出。

Night night
None

當我們捕獲到這個新異常時,我們完全沒有關于它是什么原因的上下文。為了解決這個問題,Python 3在PEP 3134中引入了顯式異常鏈接。實現它很容易。看看我們的新函數test(),這是我與上一個示例相比唯一更改的部分。

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__)

你有沒有發現我在那兒做什么?在except聲明中,我將名稱綁定e到我們捕獲的原始異常。然后,在引發新RuntimeError異常時,我將其鏈接到上一個異常,并使用from e. 我們現在的輸出...

Night night
Gravity Failing

當我們運行它時,我們的新異常會記住它是從哪里來的——前一個異常存儲在它的__cause__屬性中(打印在輸出的第二行)。這對于日志記錄特別有用。

你可以使用異常類執行許多其他技巧,尤其是在引入 PEP 3134 時。像往常一樣,我建議你閱讀文檔,我在文章末尾鏈接到該文檔。

自定義異常

Python 有一大堆異常,它們的使用有據可查。當我為工作選擇合適的異常時,我經常參考這個異常列表。然而,有時,我們只需要更多……定制的東西。

所有錯誤類型的異常都是從Exception類派生的,而類又是從BaseException類派生的。這種雙重層次結構的原因是你可以捕獲所有錯誤Exceptions,而無需對特殊的、非系統退出的異常(如KeyboardInterrupt. 當然,這在實踐中對你來說并不重要,因為except Exception實際上總是我之前提到的反模式的另一種形式。無論如何,建議你直接派生自BaseException——只要知道它存在即可。

在進行自定義異常時,你實際上可以從任何你喜歡的異常類派生。有時,最好從與你正在自定義的異常最接近的異常中獲取。但是,如果你不知所措,你可以從Exception派生.

讓我們自定義一個,好嗎?

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()

顯然,最后一個將導致我們的SpacetimeError異常被引發。

讓我們再看看那個異常類聲明。

class SpacetimeError(Exception):
    def __init__(self, message):
        super().__init__(message)

這實際上非常容易編寫。如果你還記得我們之前對類的探索,super().__init__()就是在基類上調用初始化函數,Exception在這種情況下就是這樣。我們將消息傳遞給SpacetimeError異常構造函數,并將其交給基類初始化函數。

事實上,如果我唯一要做的就是將 傳遞messagesuper(), 類,我可以讓這更簡單:

class SpacetimeError(Exception):
    pass

Python 自己處理基礎異常。

這就是我們需要做的所有事情,盡管像往常一樣,我們可以用這個做更多的技巧。自定義異常不僅僅是一個漂亮的名字;我們可以使用它們來處理各種不尋常的錯誤場景,盡管這顯然超出了本指南的范圍。

以上就是關于“Python錯誤異常怎么解決”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

乐亭县| 雅安市| 武夷山市| 泊头市| 饶河县| 双城市| 九龙县| 内江市| 津市市| 永兴县| 巴彦县| 北海市| 太和县| 资中县| 惠来县| 星座| 嘉禾县| 台州市| 英吉沙县| 龙江县| 缙云县| 贞丰县| 宁阳县| 遵义县| 图片| 云浮市| 专栏| 中宁县| 塘沽区| 岱山县| 开封县| 宜都市| 永新县| 沁阳市| 天长市| 武川县| 永吉县| 英吉沙县| 兴海县| 芦山县| 临澧县|