34. 例外処理
#34. 例外処理
例外処理は、操作が正常に完了できない場合に CPython が使用する制御フロー システムです。露骨な内容もカバーしますraiseステートメント、失敗した操作、失敗したインポート、失敗した呼び出し、ジェネレーターの終了、コンテキスト マネージャーのクリーンアップ、トレースバックの構築、およびフレームを介した伝播。
ソースレベルでは、例外は次のようになります。python id="cq34p8" try: value = risky() except ValueError: value = 0 実行時、CPython は次のことを行う必要があります。```text id="o1a48o"
execute the protected bytecode range
detect failure
record the active exception
find a matching handler
restore the frame stack to a valid state
jump to handler bytecode
run cleanup code
propagate if no handler matches
## 34.1 例外とは何か
例外は、異常な制御フローを表すオブジェクトです。
ほとんどの例外は、から派生したクラスのインスタンスです。`BaseException`。```python id="m6c3g8"
raise ValueError("bad input")
```これにより、例外オブジェクトが作成または使用され、現在の通常のパスから実行が転送されます。
例外階層は次で始まります。```text id="axuv64"
BaseException
SystemExit
KeyboardInterrupt
GeneratorExit
Exception
ValueError
TypeError
RuntimeError
OSError
...
```アプリケーションレベルの例外のほとんどは、`Exception`から直接ではありません`BaseException`。
これは次の理由から重要です。```python id="yj96ne"
except Exception:
...
```普通は釣れない`KeyboardInterrupt`、`SystemExit`、 または`GeneratorExit`。
## 34.2 通常の戻りと例外の戻り
Python 関数は、主に 2 つの方法で終了できます。```text id="ea7eqq"
normal return
exception propagation
```通常の復帰:```python id="0q9s27"
def f():
return 42
```概念的には:```text id="g7wklr"
LOAD_CONST 42
RETURN_VALUE
```例外終了:```python id="h98nnl"
def f():
raise ValueError("bad")
```概念的には:```text id="zpp0ri"
create ValueError object
set exception state
unwind frame
```発信者は次のいずれかを受信します。```text id="szzwfe"
a returned PyObject pointer
or an error indication with exception state set
```C API レベルでは、多くの関数が次の形式に従います。```c id="b99eij"
PyObject *result = some_operation();
if (result == NULL) {
/* exception is set */
return NULL;
}
```の`NULL`リターン信号の失敗。実際の例外はインタープリタ スレッドの状態に保存されます。
## 34.3 制御フローとしての例外
例外はエラーに使用されるだけでなく、構造化された制御フローにも使用されます。
例:```text id="kfz3yw"
StopIteration ends iteration
StopAsyncIteration ends async iteration
GeneratorExit closes generators
KeyboardInterrupt interrupts execution
SystemExit requests interpreter exit
ImportError reports import failure
AttributeError reports missing attributes
```の`for`ループは~に依存します`StopIteration`内部的に:```python id="79e3jp"
for x in xs:
body(x)
```概念的には:```text id="9mx6jc"
iterator = iter(xs)
while true:
try:
x = next(iterator)
except StopIteration:
break
body(x)
```したがって、例外は通常のインタープリタ プロトコルの一部です。
## 34.4 例外の発生
あ`raise`ステートメントは制御を例外機構に移します。```python id="ctu8dt"
raise ValueError("bad")
```バイトコードは大まかに次のことを実行します。```text id="h4k87k"
load ValueError
load "bad"
call ValueError("bad")
raise resulting exception
```レイズでは以下を使用できます。```python id="zewoqj"
raise SomeError
raise SomeError("message")
raise existing_exception
raise
```裸の`raise`は、アクティブな例外を処理している場合にのみ有効です。```python id="3qrtpf"
try:
risky()
except ValueError:
raise
```現在処理されている例外を再発生させます。
## 34.5 例外クラスと例外インスタンス
Python では、例外クラスまたは例外インスタンスを発生させることができます。```python id="q6npyl"
raise ValueError
```CPython は、必要に応じてこれをインスタンスに正規化します。```python id="z91m3v"
raise ValueError("bad")
```すでにインスタンスを提供しています。
内部的には、例外処理には正規化されたトリプルまたは同等の状態が必要になることがよくあります。```text id="dk5r5k"
exception type
exception value
traceback
```最新の CPython は、内部例外構造を通じてこの状態を表し、管理しますが、概念モデルは依然として有用です。
## 34.6 トレースバック
トレースバックは、例外がどこに到達したかを記録します。
例:```python id="1mo1jg"
def a():
b()
def b():
c()
def c():
1 / 0
a()
```トレースバックには、アクティブなフレームのエントリが含まれています。```text id="83c801"
a
b
c
ZeroDivisionError
```各トレースバック エントリは以下を参照します。```text id="qjfi2r"
frame
code object
instruction position
source line information
next traceback entry
```トレースバックは、単なるフォーマットされたテキストではなく、構造化されたランタイム データです。
これが、例外がローカル変数を間接的に保持できる理由です。```text id="yoeiyb"
exception
traceback
frame
locals
```## 34.7 フレームの巻き戻し
現在のフレームで例外が処理されない場合、CPython はフレームを巻き戻し、例外を呼び出し元に伝播します。
例:```python id="s11efk"
def f():
raise ValueError
def g():
f()
def h():
g()
```ハンドラーが存在しない場合:```text id="eeh2v4"
frame f raises
frame f unwinds
frame g receives exception
frame g unwinds
frame h receives exception
frame h unwinds
top level prints traceback
```ハンドラーが存在する場合`g`、伝播はそこで止まります。```python id="5x9m87"
def g():
try:
f()
except ValueError:
return 0
```例外ハンドラーが新しい制御フローのターゲットになります。
## 34.8 例外テーブル
最新の CPython は、コード オブジェクトに関連付けられた例外テーブルを使用して、保護された領域とハンドラーを記述します。
あ`try`声明:```python id="pl5k0d"
try:
risky()
except ValueError:
recover()
```コンパイルすると次のようになります。```text id="p4ajhn"
bytecode for risky()
bytecode for handler
exception table mapping protected range to handler
```例外テーブルには、次のような情報が記録されます。```text id="ca8e5f"
protected bytecode start
protected bytecode end
handler target
stack depth to restore
handler kind
```例外が発生すると、インタプリタは現在の命令位置を使用してテーブル内でハンドラを検索します。
これにより、通常の実行のために一部の古いブロック スタック機構を維持する必要がなくなり、例外が発生しないときのゼロコスト例外領域のオーバーヘッドが回避されます。
## 34.9 スタックの復元
一時値がフレーム スタック上にあるときに例外が発生する可能性があります。
例:```python id="iz2gr7"
x = f(g(), h())
```もし`h()`を発生させる場合、スタックには以下が含まれる場合があります。```text id="q5o3qr"
f
result_of_g
```への呼び出し`f`決して起こらない。 CPython は一時参照を解放し、例外ハンドラーが予期する深さにスタックを復元する必要があります。
例外テーブルのメタデータは、ハンドラーに入る前に復元するスタックの深さをインタープリターに指示します。
これは正確さのために不可欠です。ハンドラーは既知のスタック形状で開始する必要があります。
## 34.10 例外のマッチング
アン`except`句は、アクティブな例外が型または型のタプルと一致するかどうかをチェックします。```python id="ofe6wa"
try:
risky()
except ValueError:
handle()
```例外が次のインスタンスである場合、ハンドラーは一致します。`ValueError`またはサブクラス。
タプルマッチング:```python id="3jtudw"
except (ValueError, TypeError):
handle()
```どちらのタイプにも一致します。
マッチング操作では、例外クラスの関係が使用されます。文字列比較ではありません。
## 34.11 ハンドラーの順序
ハンドラーはソース順にテストされます。```python id="dxve0m"
try:
risky()
except Exception:
handle_general()
except ValueError:
handle_value()
```の`ValueError`ハンドラーが到達不能であるため、`ValueError`のサブクラスです`Exception`。
正しい順序では、特定のハンドラーが最初に配置されます。```python id="6s99w7"
try:
risky()
except ValueError:
handle_value()
except Exception:
handle_general()
```コンパイラは通常、到達不能な例外ハンドラを拒否しません。ランタイムは指定された順序に従います。
## 34.12 例外変数のバインド
アン`except ... as name`句は例外オブジェクトをバインドします。```python id="gn531x"
try:
risky()
except ValueError as exc:
print(exc)
```ハンドラー内:```text id="1csxmt"
exc -> exception instance
```ハンドラーの後、Python はこのバインディングをクリアして、トレースバックを伴う参照サイクルを削減します。
概念的には:```python id="lqlby5"
except ValueError as exc:
...
finally:
del exc
```これにより、一般的な保持サイクルが防止されます。```text id="bi9brc"
exception
traceback
frame
locals
exception
```## 34.13 ベア`except`裸のハンドラーはほとんどすべてをキャッチします。```python id="cfaisv"
try:
risky()
except:
handle()
```以下から派生した例外をキャッチします。`BaseException`、 含む`KeyboardInterrupt`そして`SystemExit`。
通常、これは範囲が広すぎます。
好む:```python id="8l0yll"
except Exception:
handle()
```通常のアプリケーションエラーを処理するとき。
裸の`except`コードがクリーンアップを実行してから再生成する必要がある場合には、依然として適切である可能性があります。```python id="2sx6wj"
try:
risky()
except:
cleanup()
raise
```##34.14`else`ブロック
あ`try`ステートメントには次のものを含めることができます`else`ブロック。```python id="ne9sbh"
try:
value = parse()
except ValueError:
value = default
else:
use(value)
```の`else`ブロックは次の場合にのみ実行されます。`try`ブロックは例外なく完了しました。
次の場合は実行されません。```text id="tmvff1"
the try block raises
the try block returns
the try block breaks
the try block continues
```バイトコード レベルでは、これはハンドラー ブロック周辺の通常の分岐制御フローです。
##34.15`finally`ブロック
あ`finally`制御が離れるとブロックが実行されます。`try`ブロック。```python id="sk93di"
try:
risky()
finally:
cleanup()
```クリーンアップは以下に対して実行されます。```text id="6kjx4b"
normal fallthrough
return
exception
break
continue
```例:```python id="pkbx1h"
def f():
try:
return 1
finally:
print("cleanup")
```返品は保留中です`finally`体が走る。
もし`finally`body が発生すると、保留中のリターンが置き換えられます。```python id="a8odfm"
def f():
try:
return 1
finally:
raise RuntimeError("cleanup failed")
```この関数は`RuntimeError`返す代わりに`1`。
##34.16`return`内部`finally`あ`return`内部`finally`以前の例外または戻り値をオーバーライドします。```python id="lip58q"
def f():
try:
raise ValueError("bad")
finally:
return 10
```これは戻ります`10`。の`ValueError`抑制されます。
この行為は合法ですが危険です。エラーを隠すことができます。
実行時には、`finally`ブロックは最終的な出口パスを制御します。戻り値が返された場合、その戻り値が関数の結果になります。
## 34.17 コンテキストマネージャーと例外
あ`with`ステートメントは例外処理に基づいて構築されています。```python id="0710p4"
with manager as value:
body(value)
```概念的には:```python id="nd0spg"
mgr = manager
exit = mgr.__exit__
value = mgr.__enter__()
try:
body(value)
except BaseException as exc:
suppress = exit(type(exc), exc, exc.__traceback__)
if not suppress:
raise
else:
exit(None, None, None)
```もし`__exit__`true 値を返すと、例外は抑制されます。
これは、コンテキスト マネージャーがトランザクションのロールバック、ファイルのクローズ、ロック、一時的な状態、およびリソースのクリーンアップを実装する方法です。
##34.18`with`バイトコード
あ`with`ステートメントは次のようなバイトコードにコンパイルされます。```text id="x3hc42"
loads the context manager
calls __enter__
stores the as-target
executes the body
calls __exit__ on normal exit
calls __exit__ on exceptional exit
suppresses or reraises based on return value
```インタプリタは呼び出すために十分な状態を保持する必要があります`__exit__`体が上がっても。
これにより、`with`構造化された形式の`try/finally`加えて例外の抑制。
## 34.19 連鎖例外
Python は例外コンテキストを記録します。
例:```python id="vpsoic"
try:
int("x")
except ValueError:
raise RuntimeError("parse failed")
```の`RuntimeError`オリジナルを指すコンテキストがあります`ValueError`。
トレースバック出力は次のようになります。```text id="9g0agc"
During handling of the above exception, another exception occurred
```明示的なチェーンの使用`from`:
```python id="iw2vuv"
raise RuntimeError("parse failed") from exc
```コンテキストの抑制には次のものが使用されます。```python id="gg994p"
raise RuntimeError("parse failed") from None
```例外オブジェクトには次のようなフィールドがあります。```text id="pqfm3s"
__context__
__cause__
__suppress_context__
__traceback__
```## 34.20 スレッド状態の例外状態
アクティブな例外は、インタープリター スレッドの状態に保存されます。
これが、C 関数がリターンによって失敗を通知できる理由です。`NULL`例外は別の場所に残しておきます。
概念的には:```text id="y794el"
thread state
current exception
handled exception stack
```C ヘルパーが失敗した場合:```c id="gctod1"
PyErr_SetString(PyExc_ValueError, "bad");
return NULL;
```呼び出し元は戻り値をチェックし、例外が設定されていることを認識します。
その後、評価ループがそれを伝播または処理します。
## 34.21 C API エラー規約
CPython C API 関数は通常、いくつかのエラー規則のいずれかに従います。
ポインタを返す関数:```c id="f6xl1n"
PyObject *obj = PyLong_FromString(text, NULL, 10);
if (obj == NULL) {
return NULL;
}
```整数を返す関数:```c id="tj37v0"
int rc = PyObject_SetAttrString(obj, "x", value);
if (rc < 0) {
return NULL;
}
```ブール値のようなチェックが返される場合があります`1`、`0`、 または`-1`:
```c id="71hl88"
int ok = PyObject_IsTrue(obj);
if (ok < 0) {
return NULL;
}
```エラー結果と例外状態は一致する必要があります。例外を設定せずにエラー コードを返す場合、通常はバグです。
## 34.22 C からのレイズ
C コードは、エラー状態を設定することによって Python 例外を発生させます。
例:```c id="c8dwxx"
PyErr_SetString(PyExc_TypeError, "expected integer");
return NULL;
```フォーマットされたメッセージの場合:```c id="npt9uu"
PyErr_Format(PyExc_ValueError, "bad value: %d", value);
return NULL;
```既存の例外を伝播する場合、C コードは例外を上書きせずにエラー センチネルを返します。
このパターンでは、評価ループが Python ハンドラーを見つけるか、トップレベルに終了するまで、エラーが多くの C 関数を通過します。
## 34.23 例外のクリア
場合によっては、C コードが意図的に例外を処理し、それをクリアすることがあります。
概念的には:```c id="44mmje"
PyObject *value = PyObject_GetAttrString(obj, "optional");
if (value == NULL) {
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
PyErr_Clear();
value = default_value;
}
else {
return NULL;
}
}
```間違った例外をクリアすると、本当のバグが隠れてしまう可能性があります。
Python コードにも同様のパターンがあります。```python id="rcjz53"
try:
value = obj.optional
except AttributeError:
value = default
```重要なルールは、処理する予定の例外のみをキャッチすることです。
## 34.24 例外処理中の例外
ハンドラーは別の例外を発生させることができます。```python id="gc39fq"
try:
risky()
except ValueError:
recover_badly()
```もし`recover_badly()`が発生すると、元の例外に関するコンテキストを保持しながら、新しい例外がハンドラーの通常の結果を置き換えます。
あ`finally`ブロックはクリーンアップ中にも発生する可能性があります。
CPython は、有用なトレースバック チェーンを構築するために十分な状態を保存する必要があります。
## 34.25 例外とジェネレータ
ジェネレーターは制御に例外を使用します。
ジェネレータは上昇することで終了します`StopIteration`内部的に呼び出し側に送信されます。```python id="dz6ljh"
def gen():
yield 1
g = gen()
next(g)
next(g)
```2番目`next(g)`上げる`StopIteration`。
あ`return value`ジェネレーター内では、に付加された値になります。`StopIteration`。```python id="r2gskh"
def gen():
return 42
yield
g = gen()
try:
next(g)
except StopIteration as exc:
print(exc.value)
```出力は次のとおりです。```text id="4y674v"
42
```ジェネレーターのファイナライズで使用されるもの`GeneratorExit`。
## 34.26 PEP 479 とジェネレーター
あ`StopIteration`発電機本体内で誤って上昇したものは、`RuntimeError`。
例:```python id="muj1jf"
def gen():
raise StopIteration
yield
```これにより、偶発的に発生する微妙なバグを回避できます。`StopIteration`ジェネレーターをサイレントに終了します。
ジェネレータープロトコルはまだ使用しています`StopIteration`境界で。変換はジェネレーターの実行内で適用されます。
## 34.27 例外とコルーチン
コルーチンはキャンセルや失敗にも例外パスを使用します。
待機されているコルーチンは正常に完了できます。```text id="tp92nc"
return value
```または失敗します:```text id="cvojm0"
raise exception
```キャンセルは通常、コルーチンの実行に挿入される例外によって表されます。
コルーチン フレームは、通常の値ではなく例外を使用して再開されます。
これは、非同期フレームワークが CPython のフレームの一時停止、再開、例外伝播モデルに大きく依存していることを意味します。
## 34.28 例外グループ
最新の Python には、複数の例外をまとめて表すための例外グループが含まれています。```python id="0cenmj"
raise ExceptionGroup("many", [ValueError("a"), TypeError("b")])
```それらは次のように処理されます。`except*`:
```python id="ihrw3w"
try:
raise ExceptionGroup("many", [ValueError("a"), TypeError("b")])
except* ValueError as group:
handle_values(group)
except* TypeError as group:
handle_types(group)
```これは、複数のタスクが同時に失敗する可能性がある同時プログラムの場合に重要です。
インタプリタは例外グループを分割して一致させる必要があります。`except*`ハンドラー。
##34.29`except*`
`except*`普通とは違う`except`。
普通`except`1 つのアクティブな例外に対して 1 つのハンドラを選択します。`except*`例外グループを分割し、異なるサブ例外を異なるハンドラーにルーティングできます。
概念的には:```text id="7m7ae6"
ExceptionGroup(ValueError, TypeError)
except* ValueError receives ValueError subgroup
except* TypeError receives TypeError subgroup
```一致しないサブグループは伝播を続けます。
これにより、個々の例外 ID を破棄することなく、構造化された複数エラー処理が追加されます。
## 34.30 トレースバックの保持
トレースバックはフレームを存続させます。
例:```python id="7vw5yb"
saved = None
def f():
big = bytearray(100_000_000)
raise RuntimeError
try:
f()
except RuntimeError as exc:
saved = exc
```保存された例外にはトレースバックが保持される場合があります。トレースバックはフレームを保持します。フレームはローカル変数を保持します`big`。
保持チェーン:```text id="9fqt5r"
saved exception
traceback
frame
locals
big bytearray
```これが、存続期間の長い例外が大量のメモリを保持できる理由です。
## 34.31 トレースバックのクリーニング
例外を必要以上に長く保存しないこと、またはトレースバック参照をクリアすることにより、保持を回避できます。```python id="tb2iqr"
try:
f()
except RuntimeError as exc:
handle(exc)
exc = None
```明示的なクリーンアップの場合:```python id="smrtfq"
exc.__traceback__ = None
```トレースバックはデバッグに役立つため、これは慎重に使用してください。
一般的なルールは、例外を保存するとコンテキストが保存されるということです。
##34.32`finally`および参照のクリーンアップ`finally`リソースを解放するためによく使用されます。```python id="iibrsm"
resource = acquire()
try:
use(resource)
finally:
resource.close()
```リソース管理の推奨形式は通常、コンテキスト マネージャーです。```python id="tfg64f"
with acquire() as resource:
use(resource)
```どちらの形式も例外処理機構に依存し、制御が保護されたブロックを離れるときにクリーンアップが確実に実行されます。
## 34.33 例外と`__del__`オブジェクトファイナライザーで発生した例外は特別に処理されます。```python id="6vbsh5"
class C:
def __del__(self):
raise RuntimeError("bad finalizer")
```からの例外`__del__`ガベージ コレクションが発生する時点でユーザー コードに正常に伝播できません。 CPython は、発生不可能な例外機構を通じてそれを報告します。
これが、ファイナライザーがレイズを避けるべき理由です。
## 34.34 発生不可能な例外
通常の伝播が不可能な場合には、いくつかの例外が発生します。
例:```text id="ydy6r1"
__del__ finalizers
weakref callbacks
some cleanup hooks
background finalization contexts
```CPython は、発生不可能な例外処理を通じてこれらを報告します。`sys.unraisablehook`。
これにより、不可能な制御フロー コンテキストを壊すことなく、診断情報が保存されます。
## 34.35 例外とインポート
インポートの失敗には例外が使用されます。```python id="54d93q"
import missing_module
```上げる`ModuleNotFoundError`。
インポートはいくつかの段階で失敗する可能性があります。```text id="0yjm9i"
module search
loader creation
source reading
bytecode loading
module execution
submodule import
package initialization
```モジュールの実行が発生すると、インポートはその例外で失敗します。
モジュールをインポートするとその最上位コードが実行されるため、インポート中に任意の例外が発生する可能性があります。
## 34.36 例外と属性の検索
欠落している属性の検索で発生する`AttributeError`。```python id="qcd62q"
obj.missing
```ただし、カスタム ルックアップではあらゆるものを呼び出すことができます。```python id="otwoa7"
class C:
@property
def x(self):
raise RuntimeError("failed")
```これは重要です`getattr`、`hasattr`、および動的フレームワーク。`hasattr`キャッチ`AttributeError`, 恣意的な例外ではありません。
## 34.37 例外と反復
反復使用`StopIteration`。
マニュアルに相当するもの:```python id="0pw412"
it = iter(xs)
while True:
try:
x = next(it)
except StopIteration:
break
body(x)
```バイトコード レベルでは、ループ命令は反復子の枯渇を認識し、ループから分岐します。
これは通常の予期される例外パスです。
## 34.38 例外とパターンマッチング
パターン マッチングでは、通常の不一致に対する例外を公開することなく、内部的に失敗を使用できます。```python id="vlqdr7"
match value:
case {"x": x}:
...
case _:
...
```マッチング エンジンは以下を区別する必要があります。```text id="b58239"
pattern does not match
operation raises a real exception
```失敗したパターンは次のケースに進む必要があります。実際の例外は伝播するはずです。
## 34.39 例外とバイトコード命令
多くのバイトコード命令が発生する可能性があります。
例:
|命令の種類 |失敗の可能性 |
|---|---|
|`LOAD_GLOBAL` | `NameError` |
| `LOAD_ATTR` | `AttributeError`または任意の記述子エラー |
|`BINARY_OP` | `TypeError`、`ZeroDivisionError`、ユーザー例外 |
|`CALL`|引数エラーまたは呼び出し先例外 |
|`IMPORT_NAME` | `ImportError`、任意のモジュール実行エラー |
|`FOR_ITER`|通常の枯渇以外のイテレータ例外 |
|`STORE_ATTR` | `AttributeError`、記述子エラー |
|`BUILD_LIST`|メモリ割り当ての失敗 |
評価ループでは、ほとんどの命令が失敗する可能性があることを想定する必要があります。
## 34.40 評価ループにおける例外の安全性
すべての命令の実装にはエラー パスが必要です。
簡略化:```c id="b2kgao"
PyObject *result = operation();
if (result == NULL) {
goto error;
}
push(result);
```エラー パスは次のとおりである必要があります。```text id="j5xxqd"
preserve the active exception
release temporary references
restore stack state
find a handler or unwind
update traceback information
avoid clobbering unrelated exception state
```これは、インタプリタの実装で最も難しい部分の 1 つです。
## 34.41 例外と参照カウント
例外は Python オブジェクトであるため、参照カウントされます。
トレースバック、フレーム、および例外オブジェクトはサイクルを形成できます。```text id="olvwqs"
exception
traceback
frame
locals
exception
```CPython には、これらのサイクルを短縮するために、例外変数に関する特別なクリーンアップ動作があります。循環ガベージ コレクターは、到達不能なサイクルを収集することもできます。
それでも、例外を保存するとオブジェクトの寿命を延ばすことができます。
## 34.42 例外の正規化
CPython は内部的に、具体的な例外インスタンスを持つように例外を正規化する必要があることがよくあります。
入力フォーム:```python id="dzbie3"
raise ValueError
raise ValueError("bad")
```どちらも例外の型とインスタンスになります。
正規化により、後のコードで以下を検査できるようになります。```text id="zqj7jd"
exception class
exception instance
traceback
cause
context
notes
```例外インスタンスの構築が失敗すると、例外の正規化自体が失敗する可能性があります。
## 34.43 例外メモ
Python の例外にはメモが含まれる場合があります。```python id="j1c2ow"
try:
raise ValueError("bad")
except ValueError as exc:
exc.add_note("while parsing config")
raise
```Notes は、トレースバック出力に追加の診断テキストを提供します。
これらは例外オブジェクトに保存され、一致動作は変わりません。
## 34.44 構文エラー
構文エラーも例外ですが、通常のバイトコードの実行前に発生します。```python id="7eglix"
eval("if")
```上げる`SyntaxError`。
コンパイル段階の例外には次のものがあります。```text id="11480v"
SyntaxError
IndentationError
TabError
```これらは、評価ループが結果のコードを実行する前に、解析またはコンパイル中に発生します。
## 34.45 メモリエラー
割り当て失敗による発生`MemoryError`CPython がそれを報告できるとき。
割り当て可能な操作の例:```text id="u0gk53"
creating objects
building lists
creating strings
expanding dictionaries
constructing tracebacks
formatting exception messages
```例外処理自体が割り当てを行う可能性があるため、ランタイムはメモリ不足状態を報告するときに注意する必要があります。
## 34.46 キーボード割り込みと信号`KeyboardInterrupt`通常、インタープリタが Ctrl-C などの割り込み信号を処理するときに発生します。
信号は任意の機械語命令では処理されません。 CPython は保留中の信号の状態を記録し、評価ループ内の安全なポイントをチェックします。
処理されると、インタプリタは次のエラーを発生させます。`KeyboardInterrupt`実行中のスレッド内。
これは、外部制御フローを提供する例外機構のもう 1 つの例です。
## 34.47 システムの終了`sys.exit()`上げる`SystemExit`。```python id="qyxowo"
import sys
sys.exit(1)
```捕捉されない場合、インタープリタは指定されたステータスで終了します。
これは例外であるため、キャッチされる可能性があります。```python id="yl2p70"
try:
sys.exit(1)
except SystemExit:
print("caught")
```これが理由です`SystemExit`~から直接派生する`BaseException`、とても広いです`except Exception`通常、ハンドラーはプロセスの終了を抑制しません。
## 34.48 よくある誤解
|誤解 |正しいモデル |
|---|---|
|例外はエラーのみです。また、反復、終了、キャンセル、および制御プロトコルも実装します。
|トレースバックは単なるテキストです |フレームレコードのチェーンです |
|`except Exception`すべてをキャッチします |全部は引っかからない`BaseException`サブクラス |
|`finally`常に元のエラーを保持します。利益の回復または引き上げ`finally`交換できます |
|`with`電話のみ`close`|それは呼びます`__enter__`そして`__exit__`、例外の詳細 |
|`hasattr`副作用はありません |属性検索を実行し、ユーザー コードを実行できます。
| C コードは例外オブジェクトを直接返します。通常、例外状態を設定し、エラー センチネルを返します。
|例外の保存は無害です。トレースバック、フレーム、および大規模なローカルを保持できます。
## 34.49 読書戦略
例外処理を学習するには、小さな例を逆アセンブルします。
以下から始めます:```python id="d0qfxt"
def f(x):
try:
return 10 / x
except ZeroDivisionError:
return 0
```次に、以下を検査します。```python id="lefgod"
import dis
dis.dis(f)
```利用可能な場合は、例外テーブルの出力も検査します。```python id="mj1mrk"
dis.show_code(f)
```次にテストします:```python id="fo0fk2"
try/finally
try/except/else
with statements
raise from
bare raise
generators with return
ExceptionGroup and except*
```ケースごとに、以下を追跡します。```text id="pag3xt"
where the protected range begins
where the handler begins
what stack cleanup is required
which exception is active
whether the frame returns or unwinds
what traceback is retained
```## 34.50 章の概要
CPython の例外処理は、構造化された制御フロー システムです。これは、例外オブジェクト、スレッド状態例外ストレージ、フレーム巻き戻し、トレースバック構築、例外テーブル、スタック復元、ハンドラー マッチング、およびクリーンアップ ブロックを使用します。
コアモデルは次のとおりです。```text id="huu536"
operation fails
↓
exception state is set
↓
current frame looks for a handler
↓
handler found: restore stack and jump
↓
no handler: unwind frame and propagate
↓
top level: print traceback or terminate
```例外は、バイトコードの実行、関数呼び出し、コンテキスト マネージャー、ジェネレーター、コルーチン、インポート、属性検索、C API エラー規約、信号処理、メモリ管理など、ランタイムの多くの部分を接続します。
例外を理解するということは、エラー処理と Python の制御フロー機構の主要部分の両方を理解することを意味します。