🕷️ Crawler Inspector

URL Lookup

Direct Parameter Lookup

Raw Queries and Responses

1. Shard Calculation

Query:
Response:
Calculated Shard: 16 (from laksa004)

2. Crawled Status Check

Query:
Response:

3. Robots.txt Check

Query:
Response:

4. Spam/Ban Check

Query:
Response:

5. Seen Status Check

ℹ️ Skipped - page is already crawled

🚫
NOT INDEXABLE
CRAWLED
2 days ago
🤖
ROBOTS ALLOWED

Page Info Filters

FilterStatusConditionDetails
HTTP statusPASSdownload_http_code = 200HTTP 200
Age cutoffPASSdownload_stamp > now() - 6 MONTH0.1 months ago
History dropPASSisNull(history_drop_reason)No drop reason
Spam/banPASSfh_dont_index != 1 AND ml_spam_score = 0ml_spam_score=0
CanonicalFAILmeta_canonical IS NULL OR = '' OR = src_unparsedorg,python!docs,/3/tutorial/errors.html s443

Page Details

PropertyValue
URLhttps://docs.python.org/zh-tw/3.13/tutorial/errors.html
Last Crawled2026-04-11 01:08:24 (2 days ago)
First Indexed2025-07-27 11:17:05 (8 months ago)
HTTP Status Code200
Meta Title8. 錯誤和例外 — Python 3.13.13 說明文件
Meta Description到目前為止還沒有提到錯誤訊息,但如果你嘗試運行範例,你可能會發現一些錯誤訊息。常見的(至少)兩種不同的錯誤類別為: 語法錯誤 (syntax error) 和 例外 (exception) 。 語法錯誤 (Syntax Error): 語法錯誤又稱剖析錯誤 (parsing error),它或許是學習 Python 的過程最常聽見的抱怨: 剖析器 (parser) 會重複犯錯的那一行,並用一...
Meta Canonicalorg,python!docs,/3/tutorial/errors.html s443
Boilerpipe Text
到目前為止還沒有提到錯誤訊息,但如果你嘗試運行範例,你可能會發現一些錯誤訊息。常見的(至少)兩種不同的錯誤類別為: 語法錯誤 (syntax error) 和 例外 (exception) 。 8.1. 語法錯誤 (Syntax Error) ¶ 語法錯誤又稱剖析錯誤 (parsing error),它或許是學習 Python 的過程最常聽見的抱怨: >>> while True print ( 'Hello world' ) File "<stdin>" , line 1 while True print ( 'Hello world' ) ^^^^^ SyntaxError : invalid syntax 剖析器 (parser) 會重複犯錯的那一行,並用一個小箭頭指向該行檢測到錯誤的地方,但請注意這並非都會是應該去修改的地方。此例中,錯誤是在 print() 函式中被檢測到,因為在它前面少了一個冒號 ( ':' )。 檔案名稱(此例中為 <stdin> )和列號會被印出來,所以如果訊息是來自一個檔案時,就可以知道去哪裡找問題。 8.2. 例外 (Exception) ¶ 即使一段陳述式或運算式使用了正確的語法,嘗試執行時仍可能導致錯誤。執行時檢測到的錯誤稱為 例外 ,例外不一定都很嚴重:你很快就能學會在 Python 程式中如何處理它們。不過大多數的例外不會被程式處理,並且會顯示如下的錯誤訊息: >>> 10 * ( 1 / 0 ) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> 10 * ( 1 / 0 ) ~^~ ZeroDivisionError : division by zero >>> 4 + spam * 3 Traceback (most recent call last): File "<stdin>" , line 1 , in <module> 4 + spam * 3 ^^^^ NameError : name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>" , line 1 , in <module> '2' + 2 ~~~~^~~ TypeError : can only concatenate str (not "int") to str 錯誤訊息的最後一行指示發生了什麼事。例外有不同的類型,而類型名稱會作為訊息的一部份被印出。範例中的例外類型為: ZeroDivisionError 、 NameError 和 TypeError 。作為例外類型被印出的字串,就是發生的內建例外 (built-in exception) 的名稱。所有的內建例外都是如此運作,但對於使用者自定的例外則不一定需要遵守(雖然這是一個有用的慣例)。標準例外名稱是內建的識別字 (identifier),不是保留關鍵字 (reserved keyword)。 此行其餘部分,根據例外的類型及導致例外的原因,說明例外的細節。 錯誤訊息的開頭,用堆疊回溯 (stack traceback) 的形式顯示發生例外的語境。一般來說,它含有一個列出源程式碼行 (source line) 的堆疊回溯;但它不會顯示從標準輸入中讀取的程式碼。 內建的例外 章節列出內建的例外及它們的意義。 8.3. 處理例外 ¶ 編寫程式處理選定的例外是可行的。以下範例會要求使用者輸入內容,直到有效的整數被輸入為止,但它允許使用者中斷程式(使用 Control - C 或作業系統支援的指令);請注意,由使用者產生的程式中斷會引發 KeyboardInterrupt 例外信號。 >>> while True : ... try : ... x = int ( input ( "Please enter a number: " )) ... break ... except ValueError : ... print ( "Oops! That was no valid number. Try again..." ) ... try 陳述式運作方式如下。 首先,執行 try 子句 ( try 和 except 關鍵字之間的陳述式)。 如果沒有發生例外,則 except 子句 會被跳過, try 陳述式執行完畢。 如果執行 try 子句時發生了例外,則該子句中剩下的部分會被跳過。如果例外的類型與 except 關鍵字後面的例外名稱相符,則 except 子句 被執行,然後,繼續執行 try/except 區塊之後的程式碼。 如果發生的例外未符合 except 子句 中的例外名稱,則將其傳遞到外層的 try 陳述式;如果仍無法找到處理者,則它是一個 未處理例外 (unhandled exception) ,執行將停止,並顯示錯誤訊息。 try 陳述式可以有不只一個 except 子句 ,為不同的例外指定處理者,而最多只有一個處理者會被執行。處理者只處理對應的 try 子句中發生的例外,而不會處理同一 try 陳述式裡其他處理者內的例外。一個 except 子句 可以用一組括號內的 tuple 列舉多個例外,例如: ... except ( RuntimeError , TypeError , NameError ): ... pass except 子句中的一個類別符合該類別本身或它的衍生類別的例外(但不是反過來 -- 列出一個衍生類別的 except 子句 不會符合它的基底類別的例外)。例如,以下程式碼會按照 B、C、D 的順序印出: class B ( Exception ): pass class C ( B ): pass class D ( C ): pass for cls in [ B , C , D ]: try : raise cls () except D : print ( "D" ) except C : print ( "C" ) except B : print ( "B" ) 請注意,如果 except 子句 的順序被反轉(把 except B 放到第一個),則會印出 B、B、B ­­——第一個符合的 except 子句 會被觸發。 當例外發生時,它可能有相關聯的值,也就是例外的 引數 。引數的存在與否及它的類型,是取決於例外的類型。 except 子句 可以在例外名稱後面指定一個變數。這個變數被綁定到一個例外實例 (instance),其引數通常儲存在 args 屬性中。為了方便,內建例外型別定義了 __str__() 以印出所有引數而不需顯式地取用 .args : >>> try : ... raise Exception ( 'spam' , 'eggs' ) ... except Exception as inst : ... print ( type ( inst )) # 例外的型別 ... print ( inst . args ) # 儲存在 .args 中的引數 ... print ( inst ) # __str__ 使得引數可以直接被印出, ... # 但可能在例外的子類別中被覆蓋 ... x , y = inst . args # 解包引數 ... print ( 'x =' , x ) ... print ( 'y =' , y ) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs 例外的 __str__() 輸出會被印在未處理例外訊息的最後一部分(「細節」)。 BaseException 是由全部的例外所共用的 base class。它的 subclass(子類別)之一, Exception ,則是所有非嚴重例外 (non-fatal exception) 的 base class。有些例外不是 Exception 的 subclass,而它們通常不會被處理,因為它們是用來指示程式應該終止。這些例外包括了由 sys.exit() 所引發的 SystemExit ,以及當使用者想要中斷程式時所引發的 KeyboardInterrupt 。 Exception 可以用作通配符 (wildcard) 來捕獲(幾乎)所有的例外。然而,比較好的做法是盡可能具體地說明我們打算處理的例外類型,並容許任何非預期例外的傳遞 (propagate)。 處理 Exception 的最常見模式,是先將該例外印出或記錄,然後再重新引發它(也允許一個呼叫函式 (caller) 來處理該例外): import sys try : f = open ( 'myfile.txt' ) s = f . readline () i = int ( s . strip ()) except OSError as err : print ( "OS error:" , err ) except ValueError : print ( "Could not convert data to an integer." ) except Exception as err : print ( f "Unexpected { err =} , { type ( err ) =} " ) raise try ... except 陳述式有一個選擇性的 else 子句 ,使用時,該子句必須放在所有 except 子句 之後。如果一段程式碼必須被執行,但 try 子句 又沒有引發例外時,這個子句很有用。例如: for arg in sys . argv [ 1 :]: try : f = open ( arg , 'r' ) except OSError : print ( 'cannot open' , arg ) else : print ( arg , 'has' , len ( f . readlines ()), 'lines' ) f . close () 使用 else 子句比向 try 子句添加額外的程式碼要好,因為這可以避免意外地捕獲不是由 try ... except 陳述式保護的程式碼所引發的例外。 例外的處理者不僅處理 try 子句 內立即發生的例外,還處理 try 子句 內(即使是間接地)呼叫的函式內部發生的例外。例如: >>> def this_fails (): ... x = 1 / 0 ... >>> try : ... this_fails () ... except ZeroDivisionError as err : ... print ( 'Handling run-time error:' , err ) ... Handling run-time error: division by zero 8.4. 引發例外 ¶ raise 陳述式可讓程式設計師強制引發指定的例外。例如: >>> raise NameError ( 'HiThere' ) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> raise NameError ( 'HiThere' ) NameError : HiThere raise 唯一的引數就是要引發的例外。該引數必須是一個例外實例或例外 class(衍生自 BaseException 的 class,例如 Exception 與它的 subclass)。如果一個例外 class 被傳遞,它會不含引數地呼叫它的建構函式 (constructor) ,使它被自動建立實例 (implicitly instantiated): raise ValueError # 'raise ValueError()' 的簡寫 如果你只想判斷是否引發了例外,但並不打算處理它,則可以使用簡單的 raise 陳述式來重新引發該例外: >>> try : ... raise NameError ( 'HiThere' ) ... except NameError : ... print ( 'An exception flew by!' ) ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>" , line 2 , in <module> raise NameError ( 'HiThere' ) NameError : HiThere 8.5. 例外鏈接 (Exception Chaining) ¶ 如果在 except 段落內部發生了一個未處理的例外,則它會讓這個將要被處理的例外附加在後,並將其包含在錯誤訊息中: >>> try : ... open ( "database.sqlite" ) ... except OSError : ... raise RuntimeError ( "unable to handle error" ) ... Traceback (most recent call last): File "<stdin>" , line 2 , in <module> open ( "database.sqlite" ) ~~~~^^^^^^^^^^^^^^^^^^^ FileNotFoundError : [Errno 2] No such file or directory: 'database.sqlite' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>" , line 4 , in <module> raise RuntimeError ( "unable to handle error" ) RuntimeError : unable to handle error 為了表明一個例外是另一個例外直接造成的結果, raise 陳述式容許一個選擇性的 from 子句: # exc 必須是例外實例或 None。 raise RuntimeError from exc 要變換例外時,這種方式很有用。例如: >>> def func (): ... raise ConnectionError ... >>> try : ... func () ... except ConnectionError as exc : ... raise RuntimeError ( 'Failed to open database' ) from exc ... Traceback (most recent call last): File "<stdin>" , line 2 , in <module> func () ~~~~^^ File "<stdin>" , line 2 , in func ConnectionError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>" , line 4 , in <module> raise RuntimeError ( 'Failed to open database' ) from exc RuntimeError : Failed to open database 它也容許使用慣用語 from None 來停用自動例外鏈接: >>> try : ... open ( 'database.sqlite' ) ... except OSError : ... raise RuntimeError from None ... Traceback (most recent call last): File "<stdin>" , line 4 , in <module> raise RuntimeError from None RuntimeError 更多關於鏈接機制的資訊,詳見 內建的例外 。 8.6. 使用者自定的例外 ¶ 程式可以透過建立新的例外 class 來命名自己的例外(深入了解 Python class,詳見 Class(類別) )。不論是直接還是間接地,例外通常應該從 Exception class 衍生出來。 例外 class 可被定義來做任何其他 class 能夠做的事,但通常會讓它維持簡單,只提供一些屬性,讓關於錯誤的資訊可被例外的處理者抽取出來。 大多數的例外定義,都會以「Error」作為名稱結尾,類似於標準例外的命名。 許多標準模組會定義它們自己的例外,以報告在其定義的函式中發生的錯誤。 8.7. 定義清理動作 ¶ try 陳述式有另一個選擇性子句,用於定義在所有情況下都必須被執行的清理動作。例如: >>> try : ... raise KeyboardInterrupt ... finally : ... print ( 'Goodbye, world!' ) ... Goodbye, world! Traceback (most recent call last): File "<stdin>" , line 2 , in <module> raise KeyboardInterrupt KeyboardInterrupt 如果 finally 子句存在,則 finally 子句會是 try 陳述式結束前執行的最後一項任務。不論 try 陳述式是否產生例外,都會執行 finally 子句。以下幾點將探討例外發生時,比較複雜的情況: 若一個例外發生於 try 子句的執行過程,則該例外會被某個 except 子句處理。如果該例外沒有被 except 子句處理,它會在 finally 子句執行後被重新引發。 一個例外可能發生於 except 或 else 子句的執行過程。同樣地,該例外會在 finally 子句執行後被重新引發。 如果 finally 子句執行 break 、 continue 或 return 陳述式,則例外不會被重新引發。 如果 try 陳述式遇到 break 、 continue 或 return 陳述式,則 finally 子句會在執行 break 、 continue 或 return 陳述式之前先執行。 如果 finally 子句中包含 return 陳述式,則回傳值會是來自 finally 子句的 return 陳述式的回傳值,而不是來自 try 子句的 return 陳述式的回傳值。 例如: >>> def bool_return (): ... try : ... return True ... finally : ... return False ... >>> bool_return () False 另一個比較複雜的範例: >>> def divide ( x , y ): ... try : ... result = x / y ... except ZeroDivisionError : ... print ( "division by zero!" ) ... else : ... print ( "result is" , result ) ... finally : ... print ( "executing finally clause" ) ... >>> divide ( 2 , 1 ) result is 2.0 executing finally clause >>> divide ( 2 , 0 ) division by zero! executing finally clause >>> divide ( "2" , "1" ) executing finally clause Traceback (most recent call last): File "<stdin>" , line 1 , in <module> divide ( "2" , "1" ) ~~~~~~^^^^^^^^^^ File "<stdin>" , line 3 , in divide result = x / y ~~^~~ TypeError : unsupported operand type(s) for /: 'str' and 'str' 如你所見, finally 子句在任何情況下都會被執行。兩個字串相除所引發的 TypeError 沒有被 except 子句處理,因此會在 finally 子句執行後被重新引發。 在真實應用程式中, finally 子句對於釋放外部資源(例如檔案或網路連線)很有用,無論該資源的使用是否成功。 8.8. 預定義的清理動作 ¶ 某些物件定義了在物件不再被需要時的標準清理動作,無論使用該物件的作業是成功或失敗。請看以下範例,它嘗試開啟一個檔案,並印出檔案內容至螢幕。 for line in open ( "myfile.txt" ): print ( line , end = "" ) 這段程式碼的問題在於,執行完該程式碼後,它讓檔案在一段不確定的時間內處於開啟狀態。在簡單腳本中這不是問題,但對於較大的應用程式來說可能會是個問題。 with 陳述式讓物件(例如檔案)在被使用時,能保證它們總是及時、正確地被清理。 with open ( "myfile.txt" ) as f : for line in f : print ( line , end = "" ) 陳述式執行完畢後,就算是在處理內容時遇到問題,檔案 f 總是會被關閉。和檔案一樣,提供預定義清理動作的物件會在說明文件中表明這一點。 8.9. 引發及處理多個無關的例外 ¶ 在某些情況下,必須回報已經發生的多個例外。在並行框架 (concurrency framework) 中經常會出現這種情況,當平行的 (parallel) 某些任務可能已經失效,但還有其他用例 (use case) 希望能繼續執行並收集多個例外,而不是只有引發第一個例外時。 內建的 ExceptionGroup 會包裝一個例外實例 (exception instance) 的 list(串列),使得它們可以一起被引發。由於它本身就是一個例外,因此它也可以像任何其他例外一樣被捕獲。 >>> def f (): ... excs = [ OSError ( 'error 1' ), SystemError ( 'error 2' )] ... raise ExceptionGroup ( 'there were problems' , excs ) ... >>> f () + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | f() | ~^^ | File "<stdin>", line 3, in f | raise ExceptionGroup('there were problems', excs) | ExceptionGroup: there were problems (2 sub-exceptions) +-+---------------- 1 ---------------- | OSError: error 1 +---------------- 2 ---------------- | SystemError: error 2 +------------------------------------ >>> try : ... f () ... except Exception as e : ... print ( f 'caught { type ( e ) } : { e } ' ) ... caught <class 'ExceptionGroup'>: there were problems (2 sub-exceptions) >>> 若使用 except* 代替 except ,我們可以選擇性地只處理該群組中與特定類型匹配的例外。在以下範例中,展示了一個巢狀的例外群組 (exception group),每個 except* 子句分別從該群組中提取一個特定類型的例外,同時讓所有其他的例外都傳遞到其他子句,最後再被重新引發。 >>> def f (): ... raise ExceptionGroup ( ... "group1" , ... [ ... OSError ( 1 ), ... SystemError ( 2 ), ... ExceptionGroup ( ... "group2" , ... [ ... OSError ( 3 ), ... RecursionError ( 4 ) ... ] ... ) ... ] ... ) ... >>> try : ... f () ... except * OSError as e : ... print ( "There were OSErrors" ) ... except * SystemError as e : ... print ( "There were SystemErrors" ) ... There were OSErrors There were SystemErrors + Exception Group Traceback (most recent call last): | File "<stdin>", line 2, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise ExceptionGroup( | ...<12 lines>... | ) | ExceptionGroup: group1 (1 sub-exception) +-+---------------- 1 ---------------- | ExceptionGroup: group2 (1 sub-exception) +-+---------------- 1 ---------------- | RecursionError: 4 +------------------------------------ >>> 請注意,被巢套在例外群組中的例外必須是實例,而不是類型。這是因為在實務上,這些例外通常是已經被程式引發並捕獲的例外,類似以下的模式: >>> excs = [] ... for test in tests : ... try : ... test . run () ... except Exception as e : ... excs . append ( e ) ... >>> if excs : ... raise ExceptionGroup ( "Test Failures" , excs ) ... 8.10. 用註解使例外更詳細 ¶ 當一個例外是為了被引發而建立時,它通常會伴隨著一些資訊被初始化,這些資訊描述了當下發生的錯誤。在某些情況,在例外被捕獲之後添加資訊會很有用。為此,例外具有一個 add_note(note) method(方法),它可以接受一個字串並將其添加到例外的註解清單中。標準的回溯呈現會在例外之後列出所有的註解,並按照其被添加的順序來排列。 >>> try : ... raise TypeError ( 'bad type' ) ... except Exception as e : ... e . add_note ( 'Add some information' ) ... e . add_note ( 'Add some more information' ) ... raise ... Traceback (most recent call last): File "<stdin>" , line 2 , in <module> raise TypeError ( 'bad type' ) TypeError : bad type Add some information Add some more information >>> 例如,在將例外收集到例外群組中時,我們可能希望為各個錯誤添加一些上下文的資訊。在以下範例中,群組中的每個例外都有一條註解,指示此錯誤是在何時發生。 >>> def f (): ... raise OSError ( 'operation failed' ) ... >>> excs = [] >>> for i in range ( 3 ): ... try : ... f () ... except Exception as e : ... e . add_note ( f 'Happened in Iteration { i + 1 } ' ) ... excs . append ( e ) ... >>> raise ExceptionGroup ( 'We have some problems' , excs ) + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | raise ExceptionGroup('We have some problems', excs) | ExceptionGroup: We have some problems (3 sub-exceptions) +-+---------------- 1 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 1 +---------------- 2 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 2 +---------------- 3 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 3 +------------------------------------ >>>
Markdown
[![Python logo](https://docs.python.org/zh-tw/3.13/_static/py.svg)](https://www.python.org/) 主題 ### [目錄](https://docs.python.org/zh-tw/3.13/contents.html) - [8\. 錯誤和例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html) - [8\.1. 語法錯誤 (Syntax Error)](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#syntax-errors) - [8\.2. 例外 (Exception)](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exceptions) - [8\.3. 處理例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#handling-exceptions) - [8\.4. 引發例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-exceptions) - [8\.5. 例外鏈接 (Exception Chaining)](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exception-chaining) - [8\.6. 使用者自定的例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#user-defined-exceptions) - [8\.7. 定義清理動作](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#defining-clean-up-actions) - [8\.8. 預定義的清理動作](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#predefined-clean-up-actions) - [8\.9. 引發及處理多個無關的例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions) - [8\.10. 用註解使例外更詳細](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#enriching-exceptions-with-notes) #### 上個主題 [7\. 輸入和輸出](https://docs.python.org/zh-tw/3.13/tutorial/inputoutput.html "上一章") #### 下個主題 [9\. Class(類別)](https://docs.python.org/zh-tw/3.13/tutorial/classes.html "下一章") ### 此頁面 - [回報錯誤](https://docs.python.org/zh-tw/3.13/bugs.html) - [顯示原始碼](https://github.com/python/cpython/blob/main/Doc/tutorial/errors.rst?plain=1) - [Show translation source](https://github.com/python/python-docs-zh-TW/blob/3.13/tutorial/errors.po?plain=1) ### 導航 - [索引](https://docs.python.org/zh-tw/3.13/genindex.html "總索引") - [模組](https://docs.python.org/zh-tw/3.13/py-modindex.html "Python 模組索引") \| - [下一頁](https://docs.python.org/zh-tw/3.13/tutorial/classes.html "9. Class(類別)") \| - [上一頁](https://docs.python.org/zh-tw/3.13/tutorial/inputoutput.html "7. 輸入和輸出") \| - ![Python logo](https://docs.python.org/zh-tw/3.13/_static/py.svg) - [Python](https://www.python.org/) » - [3\.13.13 Documentation](https://docs.python.org/zh-tw/3.13/index.html) » - [Python 教學](https://docs.python.org/zh-tw/3.13/tutorial/index.html) » - [8\. 錯誤和例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html) - \| - 主題 \| # 8\. 錯誤和例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#errors-and-exceptions "連結到這個標頭") 到目前為止還沒有提到錯誤訊息,但如果你嘗試運行範例,你可能會發現一些錯誤訊息。常見的(至少)兩種不同的錯誤類別為:*語法錯誤 (syntax error)* 和*例外 (exception)*。 ## 8\.1. 語法錯誤 (Syntax Error)[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#syntax-errors "連結到這個標頭") 語法錯誤又稱剖析錯誤 (parsing error),它或許是學習 Python 的過程最常聽見的抱怨: Copy ``` >>> while True print('Hello world') File "<stdin>", line 1 while True print('Hello world') ^^^^^ SyntaxError: invalid syntax ``` 剖析器 (parser) 會重複犯錯的那一行,並用一個小箭頭指向該行檢測到錯誤的地方,但請注意這並非都會是應該去修改的地方。此例中,錯誤是在 [`print()`](https://docs.python.org/zh-tw/3.13/library/functions.html#print "print") 函式中被檢測到,因為在它前面少了一個冒號 (`':'`)。 檔案名稱(此例中為 `<stdin>`)和列號會被印出來,所以如果訊息是來自一個檔案時,就可以知道去哪裡找問題。 ## 8\.2. 例外 (Exception)[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exceptions "連結到這個標頭") 即使一段陳述式或運算式使用了正確的語法,嘗試執行時仍可能導致錯誤。執行時檢測到的錯誤稱為*例外*,例外不一定都很嚴重:你很快就能學會在 Python 程式中如何處理它們。不過大多數的例外不會被程式處理,並且會顯示如下的錯誤訊息: Copy ``` >>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in <module> 10 * (1/0) ~^~ ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in <module> 4 + spam*3 ^^^^ NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> '2' + 2 ~~~~^~~ TypeError: can only concatenate str (not "int") to str ``` 錯誤訊息的最後一行指示發生了什麼事。例外有不同的類型,而類型名稱會作為訊息的一部份被印出。範例中的例外類型為:[`ZeroDivisionError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#ZeroDivisionError "ZeroDivisionError")、[`NameError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#NameError "NameError") 和 [`TypeError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#TypeError "TypeError")。作為例外類型被印出的字串,就是發生的內建例外 (built-in exception) 的名稱。所有的內建例外都是如此運作,但對於使用者自定的例外則不一定需要遵守(雖然這是一個有用的慣例)。標準例外名稱是內建的識別字 (identifier),不是保留關鍵字 (reserved keyword)。 此行其餘部分,根據例外的類型及導致例外的原因,說明例外的細節。 錯誤訊息的開頭,用堆疊回溯 (stack traceback) 的形式顯示發生例外的語境。一般來說,它含有一個列出源程式碼行 (source line) 的堆疊回溯;但它不會顯示從標準輸入中讀取的程式碼。 [內建的例外](https://docs.python.org/zh-tw/3.13/library/exceptions.html#bltin-exceptions)章節列出內建的例外及它們的意義。 ## 8\.3. 處理例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#handling-exceptions "連結到這個標頭") 編寫程式處理選定的例外是可行的。以下範例會要求使用者輸入內容,直到有效的整數被輸入為止,但它允許使用者中斷程式(使用 `Control`\-`C` 或作業系統支援的指令);請注意,由使用者產生的程式中斷會引發 [`KeyboardInterrupt`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#KeyboardInterrupt "KeyboardInterrupt") 例外信號。 Copy ``` >>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ... ``` [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式運作方式如下。 - 首先,執行 *try 子句*([`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 和 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 關鍵字之間的陳述式)。 - 如果沒有發生例外,則 *except 子句*會被跳過,[`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式執行完畢。 - 如果執行 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 子句時發生了例外,則該子句中剩下的部分會被跳過。如果例外的類型與 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 關鍵字後面的例外名稱相符,則 *except 子句*被執行,然後,繼續執行 try/except 區塊之後的程式碼。 - 如果發生的例外未符合 *except 子句* 中的例外名稱,則將其傳遞到外層的 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式;如果仍無法找到處理者,則它是一個*未處理例外 (unhandled exception)*,執行將停止,並顯示錯誤訊息。 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式可以有不只一個 *except 子句*,為不同的例外指定處理者,而最多只有一個處理者會被執行。處理者只處理對應的 try 子句中發生的例外,而不會處理同一 `try` 陳述式裡其他處理者內的例外。一個 *except 子句*可以用一組括號內的 tuple 列舉多個例外,例如: Copy ``` ... except (RuntimeError, TypeError, NameError): ... pass ``` [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 子句中的一個類別符合該類別本身或它的衍生類別的例外(但不是反過來 -- 列出一個衍生類別的 *except 子句*不會符合它的基底類別的例外)。例如,以下程式碼會按照 B、C、D 的順序印出: Copy ``` class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B") ``` 請注意,如果 *except 子句*的順序被反轉(把 `except B` 放到第一個),則會印出 B、B、B ­­——第一個符合的 *except 子句*會被觸發。 當例外發生時,它可能有相關聯的值,也就是例外的*引數*。引數的存在與否及它的類型,是取決於例外的類型。 *except 子句*可以在例外名稱後面指定一個變數。這個變數被綁定到一個例外實例 (instance),其引數通常儲存在 `args` 屬性中。為了方便,內建例外型別定義了 [`__str__()`](https://docs.python.org/zh-tw/3.13/reference/datamodel.html#object.__str__ "object.__str__") 以印出所有引數而不需顯式地取用 `.args`: Copy ``` >>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # 例外的型別 ... print(inst.args) # 儲存在 .args 中的引數 ... print(inst) # __str__ 使得引數可以直接被印出, ... # 但可能在例外的子類別中被覆蓋 ... x, y = inst.args # 解包引數 ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs ``` 例外的 [`__str__()`](https://docs.python.org/zh-tw/3.13/reference/datamodel.html#object.__str__ "object.__str__") 輸出會被印在未處理例外訊息的最後一部分(「細節」)。 [`BaseException`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#BaseException "BaseException") 是由全部的例外所共用的 base class。它的 subclass(子類別)之一,[`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception"),則是所有非嚴重例外 (non-fatal exception) 的 base class。有些例外不是 `Exception` 的 subclass,而它們通常不會被處理,因為它們是用來指示程式應該終止。這些例外包括了由 [`sys.exit()`](https://docs.python.org/zh-tw/3.13/library/sys.html#sys.exit "sys.exit") 所引發的 [`SystemExit`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#SystemExit "SystemExit"),以及當使用者想要中斷程式時所引發的 [`KeyboardInterrupt`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#KeyboardInterrupt "KeyboardInterrupt")。 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") 可以用作通配符 (wildcard) 來捕獲(幾乎)所有的例外。然而,比較好的做法是盡可能具體地說明我們打算處理的例外類型,並容許任何非預期例外的傳遞 (propagate)。 處理 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") 的最常見模式,是先將該例外印出或記錄,然後再重新引發它(也允許一個呼叫函式 (caller) 來處理該例外): Copy ``` import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error:", err) except ValueError: print("Could not convert data to an integer.") except Exception as err: print(f"Unexpected {err=}, {type(err)=}") raise ``` [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) ... [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 陳述式有一個選擇性的 *else 子句*,使用時,該子句必須放在所有 *except 子句*之後。如果一段程式碼必須被執行,但 *try 子句*又沒有引發例外時,這個子句很有用。例如: Copy ``` for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close() ``` 使用 `else` 子句比向 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 子句添加額外的程式碼要好,因為這可以避免意外地捕獲不是由 `try` ... `except` 陳述式保護的程式碼所引發的例外。 例外的處理者不僅處理 *try 子句*內立即發生的例外,還處理 *try 子句*內(即使是間接地)呼叫的函式內部發生的例外。例如: Copy ``` >>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: division by zero ``` ## 8\.4. 引發例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-exceptions "連結到這個標頭") [`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 陳述式可讓程式設計師強制引發指定的例外。例如: Copy ``` >>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module> raise NameError('HiThere') NameError: HiThere ``` [`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 唯一的引數就是要引發的例外。該引數必須是一個例外實例或例外 class(衍生自 [`BaseException`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#BaseException "BaseException") 的 class,例如 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") 與它的 subclass)。如果一個例外 class 被傳遞,它會不含引數地呼叫它的建構函式 (constructor) ,使它被自動建立實例 (implicitly instantiated): Copy ``` raise ValueError # 'raise ValueError()' 的簡寫 ``` 如果你只想判斷是否引發了例外,但並不打算處理它,則可以使用簡單的 [`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 陳述式來重新引發該例外: Copy ``` >>> try: ... raise NameError('HiThere') ... except NameError: ... print('An exception flew by!') ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in <module> raise NameError('HiThere') NameError: HiThere ``` ## 8\.5. 例外鏈接 (Exception Chaining)[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exception-chaining "連結到這個標頭") 如果在 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 段落內部發生了一個未處理的例外,則它會讓這個將要被處理的例外附加在後,並將其包含在錯誤訊息中: Copy ``` >>> try: ... open("database.sqlite") ... except OSError: ... raise RuntimeError("unable to handle error") ... Traceback (most recent call last): File "<stdin>", line 2, in <module> open("database.sqlite") ~~~~^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 4, in <module> raise RuntimeError("unable to handle error") RuntimeError: unable to handle error ``` 為了表明一個例外是另一個例外直接造成的結果,[`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 陳述式容許一個選擇性的 `from` 子句: Copy ``` # exc 必須是例外實例或 None。 raise RuntimeError from exc ``` 要變換例外時,這種方式很有用。例如: Copy ``` >>> def func(): ... raise ConnectionError ... >>> try: ... func() ... except ConnectionError as exc: ... raise RuntimeError('Failed to open database') from exc ... Traceback (most recent call last): File "<stdin>", line 2, in <module> func() ~~~~^^ File "<stdin>", line 2, in func ConnectionError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> raise RuntimeError('Failed to open database') from exc RuntimeError: Failed to open database ``` 它也容許使用慣用語 `from None` 來停用自動例外鏈接: Copy ``` >>> try: ... open('database.sqlite') ... except OSError: ... raise RuntimeError from None ... Traceback (most recent call last): File "<stdin>", line 4, in <module> raise RuntimeError from None RuntimeError ``` 更多關於鏈接機制的資訊,詳見[內建的例外](https://docs.python.org/zh-tw/3.13/library/exceptions.html#bltin-exceptions)。 ## 8\.6. 使用者自定的例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#user-defined-exceptions "連結到這個標頭") 程式可以透過建立新的例外 class 來命名自己的例外(深入了解 Python class,詳見[Class(類別)](https://docs.python.org/zh-tw/3.13/tutorial/classes.html#tut-classes))。不論是直接還是間接地,例外通常應該從 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") class 衍生出來。 例外 class 可被定義來做任何其他 class 能夠做的事,但通常會讓它維持簡單,只提供一些屬性,讓關於錯誤的資訊可被例外的處理者抽取出來。 大多數的例外定義,都會以「Error」作為名稱結尾,類似於標準例外的命名。 許多標準模組會定義它們自己的例外,以報告在其定義的函式中發生的錯誤。 ## 8\.7. 定義清理動作[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#defining-clean-up-actions "連結到這個標頭") [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式有另一個選擇性子句,用於定義在所有情況下都必須被執行的清理動作。例如: Copy ``` >>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! Traceback (most recent call last): File "<stdin>", line 2, in <module> raise KeyboardInterrupt KeyboardInterrupt ``` 如果 [`finally`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#finally) 子句存在,則 `finally` 子句會是 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式結束前執行的最後一項任務。不論 `try` 陳述式是否產生例外,都會執行 `finally` 子句。以下幾點將探討例外發生時,比較複雜的情況: - 若一個例外發生於 `try` 子句的執行過程,則該例外會被某個 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 子句處理。如果該例外沒有被 `except` 子句處理,它會在 `finally` 子句執行後被重新引發。 - 一個例外可能發生於 `except` 或 `else` 子句的執行過程。同樣地,該例外會在 `finally` 子句執行後被重新引發。 - 如果 `finally` 子句執行 [`break`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#break)、[`continue`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#continue) 或 [`return`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#return) 陳述式,則例外不會被重新引發。 - 如果 `try` 陳述式遇到 [`break`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#break)、[`continue`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#continue) 或 [`return`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#return) 陳述式,則 `finally` 子句會在執行 `break`、`continue` 或 `return` 陳述式之前先執行。 - 如果 `finally` 子句中包含 `return` 陳述式,則回傳值會是來自 `finally` 子句的 `return` 陳述式的回傳值,而不是來自 `try` 子句的 `return` 陳述式的回傳值。 例如: Copy ``` >>> def bool_return(): ... try: ... return True ... finally: ... return False ... >>> bool_return() False ``` 另一個比較複雜的範例: Copy ``` >>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print("division by zero!") ... else: ... print("result is", result) ... finally: ... print("executing finally clause") ... >>> divide(2, 1) result is 2.0 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in <module> divide("2", "1") ~~~~~~^^^^^^^^^^ File "<stdin>", line 3, in divide result = x / y ~~^~~ TypeError: unsupported operand type(s) for /: 'str' and 'str' ``` 如你所見,[`finally`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#finally) 子句在任何情況下都會被執行。兩個字串相除所引發的 [`TypeError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#TypeError "TypeError") 沒有被 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 子句處理,因此會在 `finally` 子句執行後被重新引發。 在真實應用程式中,[`finally`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#finally) 子句對於釋放外部資源(例如檔案或網路連線)很有用,無論該資源的使用是否成功。 ## 8\.8. 預定義的清理動作[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#predefined-clean-up-actions "連結到這個標頭") 某些物件定義了在物件不再被需要時的標準清理動作,無論使用該物件的作業是成功或失敗。請看以下範例,它嘗試開啟一個檔案,並印出檔案內容至螢幕。 Copy ``` for line in open("myfile.txt"): print(line, end="") ``` 這段程式碼的問題在於,執行完該程式碼後,它讓檔案在一段不確定的時間內處於開啟狀態。在簡單腳本中這不是問題,但對於較大的應用程式來說可能會是個問題。[`with`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#with) 陳述式讓物件(例如檔案)在被使用時,能保證它們總是及時、正確地被清理。 Copy ``` with open("myfile.txt") as f: for line in f: print(line, end="") ``` 陳述式執行完畢後,就算是在處理內容時遇到問題,檔案 *f* 總是會被關閉。和檔案一樣,提供預定義清理動作的物件會在說明文件中表明這一點。 ## 8\.9. 引發及處理多個無關的例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions "連結到這個標頭") 在某些情況下,必須回報已經發生的多個例外。在並行框架 (concurrency framework) 中經常會出現這種情況,當平行的 (parallel) 某些任務可能已經失效,但還有其他用例 (use case) 希望能繼續執行並收集多個例外,而不是只有引發第一個例外時。 內建的 [`ExceptionGroup`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#ExceptionGroup "ExceptionGroup") 會包裝一個例外實例 (exception instance) 的 list(串列),使得它們可以一起被引發。由於它本身就是一個例外,因此它也可以像任何其他例外一樣被捕獲。 Copy ``` >>> def f(): ... excs = [OSError('error 1'), SystemError('error 2')] ... raise ExceptionGroup('there were problems', excs) ... >>> f() + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | f() | ~^^ | File "<stdin>", line 3, in f | raise ExceptionGroup('there were problems', excs) | ExceptionGroup: there were problems (2 sub-exceptions) +-+---------------- 1 ---------------- | OSError: error 1 +---------------- 2 ---------------- | SystemError: error 2 +------------------------------------ >>> try: ... f() ... except Exception as e: ... print(f'caught {type(e)}: {e}') ... caught <class 'ExceptionGroup'>: there were problems (2 sub-exceptions) >>> ``` 若使用 `except*` 代替 `except`,我們可以選擇性地只處理該群組中與特定類型匹配的例外。在以下範例中,展示了一個巢狀的例外群組 (exception group),每個 `except*` 子句分別從該群組中提取一個特定類型的例外,同時讓所有其他的例外都傳遞到其他子句,最後再被重新引發。 Copy ``` >>> def f(): ... raise ExceptionGroup( ... "group1", ... [ ... OSError(1), ... SystemError(2), ... ExceptionGroup( ... "group2", ... [ ... OSError(3), ... RecursionError(4) ... ] ... ) ... ] ... ) ... >>> try: ... f() ... except* OSError as e: ... print("There were OSErrors") ... except* SystemError as e: ... print("There were SystemErrors") ... There were OSErrors There were SystemErrors + Exception Group Traceback (most recent call last): | File "<stdin>", line 2, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise ExceptionGroup( | ...<12 lines>... | ) | ExceptionGroup: group1 (1 sub-exception) +-+---------------- 1 ---------------- | ExceptionGroup: group2 (1 sub-exception) +-+---------------- 1 ---------------- | RecursionError: 4 +------------------------------------ >>> ``` 請注意,被巢套在例外群組中的例外必須是實例,而不是類型。這是因為在實務上,這些例外通常是已經被程式引發並捕獲的例外,類似以下的模式: Copy ``` >>> excs = [] ... for test in tests: ... try: ... test.run() ... except Exception as e: ... excs.append(e) ... >>> if excs: ... raise ExceptionGroup("Test Failures", excs) ... ``` ## 8\.10. 用註解使例外更詳細[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#enriching-exceptions-with-notes "連結到這個標頭") 當一個例外是為了被引發而建立時,它通常會伴隨著一些資訊被初始化,這些資訊描述了當下發生的錯誤。在某些情況,在例外被捕獲之後添加資訊會很有用。為此,例外具有一個 `add_note(note)` method(方法),它可以接受一個字串並將其添加到例外的註解清單中。標準的回溯呈現會在例外之後列出所有的註解,並按照其被添加的順序來排列。 Copy ``` >>> try: ... raise TypeError('bad type') ... except Exception as e: ... e.add_note('Add some information') ... e.add_note('Add some more information') ... raise ... Traceback (most recent call last): File "<stdin>", line 2, in <module> raise TypeError('bad type') TypeError: bad type Add some information Add some more information >>> ``` 例如,在將例外收集到例外群組中時,我們可能希望為各個錯誤添加一些上下文的資訊。在以下範例中,群組中的每個例外都有一條註解,指示此錯誤是在何時發生。 Copy ``` >>> def f(): ... raise OSError('operation failed') ... >>> excs = [] >>> for i in range(3): ... try: ... f() ... except Exception as e: ... e.add_note(f'Happened in Iteration {i+1}') ... excs.append(e) ... >>> raise ExceptionGroup('We have some problems', excs) + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | raise ExceptionGroup('We have some problems', excs) | ExceptionGroup: We have some problems (3 sub-exceptions) +-+---------------- 1 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 1 +---------------- 2 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 2 +---------------- 3 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 3 +------------------------------------ >>> ``` ### [目錄](https://docs.python.org/zh-tw/3.13/contents.html) - [8\. 錯誤和例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html) - [8\.1. 語法錯誤 (Syntax Error)](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#syntax-errors) - [8\.2. 例外 (Exception)](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exceptions) - [8\.3. 處理例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#handling-exceptions) - [8\.4. 引發例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-exceptions) - [8\.5. 例外鏈接 (Exception Chaining)](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exception-chaining) - [8\.6. 使用者自定的例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#user-defined-exceptions) - [8\.7. 定義清理動作](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#defining-clean-up-actions) - [8\.8. 預定義的清理動作](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#predefined-clean-up-actions) - [8\.9. 引發及處理多個無關的例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions) - [8\.10. 用註解使例外更詳細](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#enriching-exceptions-with-notes) #### 上個主題 [7\. 輸入和輸出](https://docs.python.org/zh-tw/3.13/tutorial/inputoutput.html "上一章") #### 下個主題 [9\. Class(類別)](https://docs.python.org/zh-tw/3.13/tutorial/classes.html "下一章") ### 此頁面 - [回報錯誤](https://docs.python.org/zh-tw/3.13/bugs.html) - [顯示原始碼](https://github.com/python/cpython/blob/main/Doc/tutorial/errors.rst?plain=1) - [Show translation source](https://github.com/python/python-docs-zh-TW/blob/3.13/tutorial/errors.po?plain=1) « ### 導航 - [索引](https://docs.python.org/zh-tw/3.13/genindex.html "總索引") - [模組](https://docs.python.org/zh-tw/3.13/py-modindex.html "Python 模組索引") \| - [下一頁](https://docs.python.org/zh-tw/3.13/tutorial/classes.html "9. Class(類別)") \| - [上一頁](https://docs.python.org/zh-tw/3.13/tutorial/inputoutput.html "7. 輸入和輸出") \| - ![Python logo](https://docs.python.org/zh-tw/3.13/_static/py.svg) - [Python](https://www.python.org/) » - [3\.13.13 Documentation](https://docs.python.org/zh-tw/3.13/index.html) » - [Python 教學](https://docs.python.org/zh-tw/3.13/tutorial/index.html) » - [8\. 錯誤和例外](https://docs.python.org/zh-tw/3.13/tutorial/errors.html) - \| - 主題 \| © [版權所有](https://docs.python.org/zh-tw/3.13/copyright.html) 2001-2026, Python Software Foundation. 此頁面採用 Python 軟體基金會授權條款第 2 版。 文件中的範例、應用技巧與其他程式碼額外採用了 Zero Clause BSD 授權條款。 更多訊息請見[歷史與授權條款](https://docs.python.org/license.html)。 Python 軟體基金會是一家非營利法人。 [敬請捐贈。](https://www.python.org/psf/donations/) 最後更新於 4月 10, 2026 (12:06 UTC)。 [發現 bug](https://docs.python.org/bugs.html)? 以 [Sphinx](https://www.sphinx-doc.org/)8\.2.3建立。
Readable Markdown
到目前為止還沒有提到錯誤訊息,但如果你嘗試運行範例,你可能會發現一些錯誤訊息。常見的(至少)兩種不同的錯誤類別為:*語法錯誤 (syntax error)* 和*例外 (exception)*。 ## 8\.1. 語法錯誤 (Syntax Error)[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#syntax-errors "連結到這個標頭") 語法錯誤又稱剖析錯誤 (parsing error),它或許是學習 Python 的過程最常聽見的抱怨: ``` >>> while True print('Hello world') File "<stdin>", line 1 while True print('Hello world') ^^^^^ SyntaxError: invalid syntax ``` 剖析器 (parser) 會重複犯錯的那一行,並用一個小箭頭指向該行檢測到錯誤的地方,但請注意這並非都會是應該去修改的地方。此例中,錯誤是在 [`print()`](https://docs.python.org/zh-tw/3.13/library/functions.html#print "print") 函式中被檢測到,因為在它前面少了一個冒號 (`':'`)。 檔案名稱(此例中為 `<stdin>`)和列號會被印出來,所以如果訊息是來自一個檔案時,就可以知道去哪裡找問題。 ## 8\.2. 例外 (Exception)[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exceptions "連結到這個標頭") 即使一段陳述式或運算式使用了正確的語法,嘗試執行時仍可能導致錯誤。執行時檢測到的錯誤稱為*例外*,例外不一定都很嚴重:你很快就能學會在 Python 程式中如何處理它們。不過大多數的例外不會被程式處理,並且會顯示如下的錯誤訊息: ``` >>> 10 * (1/0) Traceback (most recent call last): File "<stdin>", line 1, in <module> 10 * (1/0) ~^~ ZeroDivisionError: division by zero >>> 4 + spam*3 Traceback (most recent call last): File "<stdin>", line 1, in <module> 4 + spam*3 ^^^^ NameError: name 'spam' is not defined >>> '2' + 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> '2' + 2 ~~~~^~~ TypeError: can only concatenate str (not "int") to str ``` 錯誤訊息的最後一行指示發生了什麼事。例外有不同的類型,而類型名稱會作為訊息的一部份被印出。範例中的例外類型為:[`ZeroDivisionError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#ZeroDivisionError "ZeroDivisionError")、[`NameError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#NameError "NameError") 和 [`TypeError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#TypeError "TypeError")。作為例外類型被印出的字串,就是發生的內建例外 (built-in exception) 的名稱。所有的內建例外都是如此運作,但對於使用者自定的例外則不一定需要遵守(雖然這是一個有用的慣例)。標準例外名稱是內建的識別字 (identifier),不是保留關鍵字 (reserved keyword)。 此行其餘部分,根據例外的類型及導致例外的原因,說明例外的細節。 錯誤訊息的開頭,用堆疊回溯 (stack traceback) 的形式顯示發生例外的語境。一般來說,它含有一個列出源程式碼行 (source line) 的堆疊回溯;但它不會顯示從標準輸入中讀取的程式碼。 [內建的例外](https://docs.python.org/zh-tw/3.13/library/exceptions.html#bltin-exceptions)章節列出內建的例外及它們的意義。 ## 8\.3. 處理例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#handling-exceptions "連結到這個標頭") 編寫程式處理選定的例外是可行的。以下範例會要求使用者輸入內容,直到有效的整數被輸入為止,但它允許使用者中斷程式(使用 `Control`\-`C` 或作業系統支援的指令);請注意,由使用者產生的程式中斷會引發 [`KeyboardInterrupt`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#KeyboardInterrupt "KeyboardInterrupt") 例外信號。 ``` >>> while True: ... try: ... x = int(input("Please enter a number: ")) ... break ... except ValueError: ... print("Oops! That was no valid number. Try again...") ... ``` [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式運作方式如下。 - 首先,執行 *try 子句*([`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 和 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 關鍵字之間的陳述式)。 - 如果沒有發生例外,則 *except 子句*會被跳過,[`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式執行完畢。 - 如果執行 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 子句時發生了例外,則該子句中剩下的部分會被跳過。如果例外的類型與 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 關鍵字後面的例外名稱相符,則 *except 子句*被執行,然後,繼續執行 try/except 區塊之後的程式碼。 - 如果發生的例外未符合 *except 子句* 中的例外名稱,則將其傳遞到外層的 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式;如果仍無法找到處理者,則它是一個*未處理例外 (unhandled exception)*,執行將停止,並顯示錯誤訊息。 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式可以有不只一個 *except 子句*,為不同的例外指定處理者,而最多只有一個處理者會被執行。處理者只處理對應的 try 子句中發生的例外,而不會處理同一 `try` 陳述式裡其他處理者內的例外。一個 *except 子句*可以用一組括號內的 tuple 列舉多個例外,例如: ``` ... except (RuntimeError, TypeError, NameError): ... pass ``` [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 子句中的一個類別符合該類別本身或它的衍生類別的例外(但不是反過來 -- 列出一個衍生類別的 *except 子句*不會符合它的基底類別的例外)。例如,以下程式碼會按照 B、C、D 的順序印出: ``` class B(Exception): pass class C(B): pass class D(C): pass for cls in [B, C, D]: try: raise cls() except D: print("D") except C: print("C") except B: print("B") ``` 請注意,如果 *except 子句*的順序被反轉(把 `except B` 放到第一個),則會印出 B、B、B ­­——第一個符合的 *except 子句*會被觸發。 當例外發生時,它可能有相關聯的值,也就是例外的*引數*。引數的存在與否及它的類型,是取決於例外的類型。 *except 子句*可以在例外名稱後面指定一個變數。這個變數被綁定到一個例外實例 (instance),其引數通常儲存在 `args` 屬性中。為了方便,內建例外型別定義了 [`__str__()`](https://docs.python.org/zh-tw/3.13/reference/datamodel.html#object.__str__ "object.__str__") 以印出所有引數而不需顯式地取用 `.args`: ``` >>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # 例外的型別 ... print(inst.args) # 儲存在 .args 中的引數 ... print(inst) # __str__ 使得引數可以直接被印出, ... # 但可能在例外的子類別中被覆蓋 ... x, y = inst.args # 解包引數 ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs ``` 例外的 [`__str__()`](https://docs.python.org/zh-tw/3.13/reference/datamodel.html#object.__str__ "object.__str__") 輸出會被印在未處理例外訊息的最後一部分(「細節」)。 [`BaseException`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#BaseException "BaseException") 是由全部的例外所共用的 base class。它的 subclass(子類別)之一,[`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception"),則是所有非嚴重例外 (non-fatal exception) 的 base class。有些例外不是 `Exception` 的 subclass,而它們通常不會被處理,因為它們是用來指示程式應該終止。這些例外包括了由 [`sys.exit()`](https://docs.python.org/zh-tw/3.13/library/sys.html#sys.exit "sys.exit") 所引發的 [`SystemExit`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#SystemExit "SystemExit"),以及當使用者想要中斷程式時所引發的 [`KeyboardInterrupt`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#KeyboardInterrupt "KeyboardInterrupt")。 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") 可以用作通配符 (wildcard) 來捕獲(幾乎)所有的例外。然而,比較好的做法是盡可能具體地說明我們打算處理的例外類型,並容許任何非預期例外的傳遞 (propagate)。 處理 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") 的最常見模式,是先將該例外印出或記錄,然後再重新引發它(也允許一個呼叫函式 (caller) 來處理該例外): ``` import sys try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error:", err) except ValueError: print("Could not convert data to an integer.") except Exception as err: print(f"Unexpected {err=}, {type(err)=}") raise ``` [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) ... [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 陳述式有一個選擇性的 *else 子句*,使用時,該子句必須放在所有 *except 子句*之後。如果一段程式碼必須被執行,但 *try 子句*又沒有引發例外時,這個子句很有用。例如: ``` for arg in sys.argv[1:]: try: f = open(arg, 'r') except OSError: print('cannot open', arg) else: print(arg, 'has', len(f.readlines()), 'lines') f.close() ``` 使用 `else` 子句比向 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 子句添加額外的程式碼要好,因為這可以避免意外地捕獲不是由 `try` ... `except` 陳述式保護的程式碼所引發的例外。 例外的處理者不僅處理 *try 子句*內立即發生的例外,還處理 *try 子句*內(即使是間接地)呼叫的函式內部發生的例外。例如: ``` >>> def this_fails(): ... x = 1/0 ... >>> try: ... this_fails() ... except ZeroDivisionError as err: ... print('Handling run-time error:', err) ... Handling run-time error: division by zero ``` ## 8\.4. 引發例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-exceptions "連結到這個標頭") [`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 陳述式可讓程式設計師強制引發指定的例外。例如: ``` >>> raise NameError('HiThere') Traceback (most recent call last): File "<stdin>", line 1, in <module> raise NameError('HiThere') NameError: HiThere ``` [`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 唯一的引數就是要引發的例外。該引數必須是一個例外實例或例外 class(衍生自 [`BaseException`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#BaseException "BaseException") 的 class,例如 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") 與它的 subclass)。如果一個例外 class 被傳遞,它會不含引數地呼叫它的建構函式 (constructor) ,使它被自動建立實例 (implicitly instantiated): ``` raise ValueError # 'raise ValueError()' 的簡寫 ``` 如果你只想判斷是否引發了例外,但並不打算處理它,則可以使用簡單的 [`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 陳述式來重新引發該例外: ``` >>> try: ... raise NameError('HiThere') ... except NameError: ... print('An exception flew by!') ... raise ... An exception flew by! Traceback (most recent call last): File "<stdin>", line 2, in <module> raise NameError('HiThere') NameError: HiThere ``` ## 8\.5. 例外鏈接 (Exception Chaining)[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#exception-chaining "連結到這個標頭") 如果在 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 段落內部發生了一個未處理的例外,則它會讓這個將要被處理的例外附加在後,並將其包含在錯誤訊息中: ``` >>> try: ... open("database.sqlite") ... except OSError: ... raise RuntimeError("unable to handle error") ... Traceback (most recent call last): File "<stdin>", line 2, in <module> open("database.sqlite") ~~~~^^^^^^^^^^^^^^^^^^^ FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "<stdin>", line 4, in <module> raise RuntimeError("unable to handle error") RuntimeError: unable to handle error ``` 為了表明一個例外是另一個例外直接造成的結果,[`raise`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#raise) 陳述式容許一個選擇性的 `from` 子句: ``` # exc 必須是例外實例或 None。 raise RuntimeError from exc ``` 要變換例外時,這種方式很有用。例如: ``` >>> def func(): ... raise ConnectionError ... >>> try: ... func() ... except ConnectionError as exc: ... raise RuntimeError('Failed to open database') from exc ... Traceback (most recent call last): File "<stdin>", line 2, in <module> func() ~~~~^^ File "<stdin>", line 2, in func ConnectionError The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<stdin>", line 4, in <module> raise RuntimeError('Failed to open database') from exc RuntimeError: Failed to open database ``` 它也容許使用慣用語 `from None` 來停用自動例外鏈接: ``` >>> try: ... open('database.sqlite') ... except OSError: ... raise RuntimeError from None ... Traceback (most recent call last): File "<stdin>", line 4, in <module> raise RuntimeError from None RuntimeError ``` 更多關於鏈接機制的資訊,詳見[內建的例外](https://docs.python.org/zh-tw/3.13/library/exceptions.html#bltin-exceptions)。 ## 8\.6. 使用者自定的例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#user-defined-exceptions "連結到這個標頭") 程式可以透過建立新的例外 class 來命名自己的例外(深入了解 Python class,詳見[Class(類別)](https://docs.python.org/zh-tw/3.13/tutorial/classes.html#tut-classes))。不論是直接還是間接地,例外通常應該從 [`Exception`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#Exception "Exception") class 衍生出來。 例外 class 可被定義來做任何其他 class 能夠做的事,但通常會讓它維持簡單,只提供一些屬性,讓關於錯誤的資訊可被例外的處理者抽取出來。 大多數的例外定義,都會以「Error」作為名稱結尾,類似於標準例外的命名。 許多標準模組會定義它們自己的例外,以報告在其定義的函式中發生的錯誤。 ## 8\.7. 定義清理動作[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#defining-clean-up-actions "連結到這個標頭") [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式有另一個選擇性子句,用於定義在所有情況下都必須被執行的清理動作。例如: ``` >>> try: ... raise KeyboardInterrupt ... finally: ... print('Goodbye, world!') ... Goodbye, world! Traceback (most recent call last): File "<stdin>", line 2, in <module> raise KeyboardInterrupt KeyboardInterrupt ``` 如果 [`finally`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#finally) 子句存在,則 `finally` 子句會是 [`try`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#try) 陳述式結束前執行的最後一項任務。不論 `try` 陳述式是否產生例外,都會執行 `finally` 子句。以下幾點將探討例外發生時,比較複雜的情況: - 若一個例外發生於 `try` 子句的執行過程,則該例外會被某個 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 子句處理。如果該例外沒有被 `except` 子句處理,它會在 `finally` 子句執行後被重新引發。 - 一個例外可能發生於 `except` 或 `else` 子句的執行過程。同樣地,該例外會在 `finally` 子句執行後被重新引發。 - 如果 `finally` 子句執行 [`break`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#break)、[`continue`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#continue) 或 [`return`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#return) 陳述式,則例外不會被重新引發。 - 如果 `try` 陳述式遇到 [`break`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#break)、[`continue`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#continue) 或 [`return`](https://docs.python.org/zh-tw/3.13/reference/simple_stmts.html#return) 陳述式,則 `finally` 子句會在執行 `break`、`continue` 或 `return` 陳述式之前先執行。 - 如果 `finally` 子句中包含 `return` 陳述式,則回傳值會是來自 `finally` 子句的 `return` 陳述式的回傳值,而不是來自 `try` 子句的 `return` 陳述式的回傳值。 例如: ``` >>> def bool_return(): ... try: ... return True ... finally: ... return False ... >>> bool_return() False ``` 另一個比較複雜的範例: ``` >>> def divide(x, y): ... try: ... result = x / y ... except ZeroDivisionError: ... print("division by zero!") ... else: ... print("result is", result) ... finally: ... print("executing finally clause") ... >>> divide(2, 1) result is 2.0 executing finally clause >>> divide(2, 0) division by zero! executing finally clause >>> divide("2", "1") executing finally clause Traceback (most recent call last): File "<stdin>", line 1, in <module> divide("2", "1") ~~~~~~^^^^^^^^^^ File "<stdin>", line 3, in divide result = x / y ~~^~~ TypeError: unsupported operand type(s) for /: 'str' and 'str' ``` 如你所見,[`finally`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#finally) 子句在任何情況下都會被執行。兩個字串相除所引發的 [`TypeError`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#TypeError "TypeError") 沒有被 [`except`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#except) 子句處理,因此會在 `finally` 子句執行後被重新引發。 在真實應用程式中,[`finally`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#finally) 子句對於釋放外部資源(例如檔案或網路連線)很有用,無論該資源的使用是否成功。 ## 8\.8. 預定義的清理動作[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#predefined-clean-up-actions "連結到這個標頭") 某些物件定義了在物件不再被需要時的標準清理動作,無論使用該物件的作業是成功或失敗。請看以下範例,它嘗試開啟一個檔案,並印出檔案內容至螢幕。 ``` for line in open("myfile.txt"): print(line, end="") ``` 這段程式碼的問題在於,執行完該程式碼後,它讓檔案在一段不確定的時間內處於開啟狀態。在簡單腳本中這不是問題,但對於較大的應用程式來說可能會是個問題。[`with`](https://docs.python.org/zh-tw/3.13/reference/compound_stmts.html#with) 陳述式讓物件(例如檔案)在被使用時,能保證它們總是及時、正確地被清理。 ``` with open("myfile.txt") as f: for line in f: print(line, end="") ``` 陳述式執行完畢後,就算是在處理內容時遇到問題,檔案 *f* 總是會被關閉。和檔案一樣,提供預定義清理動作的物件會在說明文件中表明這一點。 ## 8\.9. 引發及處理多個無關的例外[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions "連結到這個標頭") 在某些情況下,必須回報已經發生的多個例外。在並行框架 (concurrency framework) 中經常會出現這種情況,當平行的 (parallel) 某些任務可能已經失效,但還有其他用例 (use case) 希望能繼續執行並收集多個例外,而不是只有引發第一個例外時。 內建的 [`ExceptionGroup`](https://docs.python.org/zh-tw/3.13/library/exceptions.html#ExceptionGroup "ExceptionGroup") 會包裝一個例外實例 (exception instance) 的 list(串列),使得它們可以一起被引發。由於它本身就是一個例外,因此它也可以像任何其他例外一樣被捕獲。 ``` >>> def f(): ... excs = [OSError('error 1'), SystemError('error 2')] ... raise ExceptionGroup('there were problems', excs) ... >>> f() + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | f() | ~^^ | File "<stdin>", line 3, in f | raise ExceptionGroup('there were problems', excs) | ExceptionGroup: there were problems (2 sub-exceptions) +-+---------------- 1 ---------------- | OSError: error 1 +---------------- 2 ---------------- | SystemError: error 2 +------------------------------------ >>> try: ... f() ... except Exception as e: ... print(f'caught {type(e)}: {e}') ... caught <class 'ExceptionGroup'>: there were problems (2 sub-exceptions) >>> ``` 若使用 `except*` 代替 `except`,我們可以選擇性地只處理該群組中與特定類型匹配的例外。在以下範例中,展示了一個巢狀的例外群組 (exception group),每個 `except*` 子句分別從該群組中提取一個特定類型的例外,同時讓所有其他的例外都傳遞到其他子句,最後再被重新引發。 ``` >>> def f(): ... raise ExceptionGroup( ... "group1", ... [ ... OSError(1), ... SystemError(2), ... ExceptionGroup( ... "group2", ... [ ... OSError(3), ... RecursionError(4) ... ] ... ) ... ] ... ) ... >>> try: ... f() ... except* OSError as e: ... print("There were OSErrors") ... except* SystemError as e: ... print("There were SystemErrors") ... There were OSErrors There were SystemErrors + Exception Group Traceback (most recent call last): | File "<stdin>", line 2, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise ExceptionGroup( | ...<12 lines>... | ) | ExceptionGroup: group1 (1 sub-exception) +-+---------------- 1 ---------------- | ExceptionGroup: group2 (1 sub-exception) +-+---------------- 1 ---------------- | RecursionError: 4 +------------------------------------ >>> ``` 請注意,被巢套在例外群組中的例外必須是實例,而不是類型。這是因為在實務上,這些例外通常是已經被程式引發並捕獲的例外,類似以下的模式: ``` >>> excs = [] ... for test in tests: ... try: ... test.run() ... except Exception as e: ... excs.append(e) ... >>> if excs: ... raise ExceptionGroup("Test Failures", excs) ... ``` ## 8\.10. 用註解使例外更詳細[¶](https://docs.python.org/zh-tw/3.13/tutorial/errors.html#enriching-exceptions-with-notes "連結到這個標頭") 當一個例外是為了被引發而建立時,它通常會伴隨著一些資訊被初始化,這些資訊描述了當下發生的錯誤。在某些情況,在例外被捕獲之後添加資訊會很有用。為此,例外具有一個 `add_note(note)` method(方法),它可以接受一個字串並將其添加到例外的註解清單中。標準的回溯呈現會在例外之後列出所有的註解,並按照其被添加的順序來排列。 ``` >>> try: ... raise TypeError('bad type') ... except Exception as e: ... e.add_note('Add some information') ... e.add_note('Add some more information') ... raise ... Traceback (most recent call last): File "<stdin>", line 2, in <module> raise TypeError('bad type') TypeError: bad type Add some information Add some more information >>> ``` 例如,在將例外收集到例外群組中時,我們可能希望為各個錯誤添加一些上下文的資訊。在以下範例中,群組中的每個例外都有一條註解,指示此錯誤是在何時發生。 ``` >>> def f(): ... raise OSError('operation failed') ... >>> excs = [] >>> for i in range(3): ... try: ... f() ... except Exception as e: ... e.add_note(f'Happened in Iteration {i+1}') ... excs.append(e) ... >>> raise ExceptionGroup('We have some problems', excs) + Exception Group Traceback (most recent call last): | File "<stdin>", line 1, in <module> | raise ExceptionGroup('We have some problems', excs) | ExceptionGroup: We have some problems (3 sub-exceptions) +-+---------------- 1 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 1 +---------------- 2 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 2 +---------------- 3 ---------------- | Traceback (most recent call last): | File "<stdin>", line 3, in <module> | f() | ~^^ | File "<stdin>", line 2, in f | raise OSError('operation failed') | OSError: operation failed | Happened in Iteration 3 +------------------------------------ >>> ```
Shard16 (laksa)
Root Hash10954876678907435016
Unparsed URLorg,python!docs,/zh-tw/3.13/tutorial/errors.html s443