ℹ️ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 0.1 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | FAIL | meta_canonical IS NULL OR = '' OR = src_unparsed | org,python!docs,/3/tutorial/errors.html s443 |
| Property | Value |
|---|---|
| URL | https://docs.python.org/zh-tw/3.13/tutorial/errors.html |
| Last Crawled | 2026-04-11 01:08:24 (2 days ago) |
| First Indexed | 2025-07-27 11:17:05 (8 months ago) |
| HTTP Status Code | 200 |
| Meta Title | 8. 錯誤和例外 — Python 3.13.13 說明文件 |
| Meta Description | 到目前為止還沒有提到錯誤訊息,但如果你嘗試運行範例,你可能會發現一些錯誤訊息。常見的(至少)兩種不同的錯誤類別為: 語法錯誤 (syntax error) 和 例外 (exception) 。 語法錯誤 (Syntax Error): 語法錯誤又稱剖析錯誤 (parsing error),它或許是學習 Python 的過程最常聽見的抱怨: 剖析器 (parser) 會重複犯錯的那一行,並用一... |
| Meta Canonical | org,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 | [](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](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](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
+------------------------------------
>>>
``` |
| Shard | 16 (laksa) |
| Root Hash | 10954876678907435016 |
| Unparsed URL | org,python!docs,/zh-tw/3.13/tutorial/errors.html s443 |