28. フレーム
#28. フレーム
フレームは、Python コードの 1 回のアクティブな実行の実行時レコードです。 CPython は、Python 関数の呼び出し、モジュール本体の実行、クラス本体の実行、ジェネレーターの再開、またはコルーチンの再開を行うときに、フレームのような実行レコードを使用して現在の状態を保持します。
コード オブジェクトは、何を実行するかを示します。
フレームには、現在どこで実行されているか、および現在どのような値が生きているかが示されます。text code object = immutable instructions and metadata frame = mutable execution state for one run of that code この関数の場合:```python
def add(a, b):
c = a + b
return c
のために作られたフレーム`add(2, 3)`現在の引数値、ローカル変数スロット、スタック値、命令位置、例外状態、および実行コンテキストへのリンクが含まれます。
## 28.1 フレームが存在する理由
Python プログラムでは、同時に多数のアクティブな呼び出しを行うことができます。```python
def a():
return b()
def b():
return c()
def c():
return 42
a()
```実行中、CPython は呼び出し先の実行中に一時停止された各呼び出し元を記憶する必要があります。
概念的には:```text
frame for a
waiting for b
frame for b
waiting for c
frame for c
currently executing
```各フレームには、ネストされた呼び出しが戻った後にコードを再開するのに十分な状態が保存されます。
フレームは次の質問に答えます。```text
Which code object is running?
Which instruction is next?
What are the local variables?
What temporary values are on the stack?
Which globals and builtins are visible?
What exception is being handled?
Is tracing or profiling active?
Who called this frame?
```## 28.2 コードオブジェクトとフレーム
コードオブジェクトは再利用可能です。フレームは 1 つの実行インスタンスです。```python
def f(x):
return x + 1
a = f(10)
b = f(20)
```どちらの呼び出しも同じコード オブジェクトを使用します。```python
print(f.__code__)
```ただし、各呼び出しには独自のフレーム状態があります。
|コンセプト |コードオブジェクト |フレーム |
|---|---|---|
|可変性 |ほとんど不変 |可変 |
|生涯 |多くの場合長命です |通常は 1 回の呼び出しまたは一時停止された実行 |
|バイトコードが含まれています |はい |コードオブジェクトを参照する |
|地元の人が含まれています |いいえ |はい |
|スタックが含まれています |いいえ |はい |
|命令ポインタが含まれています |いいえ |はい |
|通話間で共有 |はい |いいえ |
コード オブジェクトはプログラムの断片です。フレームは、そのフラグメントの実行中のアクティブ化です。
## 28.3 関数フレーム
関数呼び出しは、呼び出し先のフレームを作成または初期化します。```python
def square(x):
return x * x
square(9)
```関数オブジェクトには次のものが含まれます。```text
code object
globals
defaults
keyword defaults
closure cells
annotations
qualname
module name
```呼び出されると、CPython は関数オブジェクトと実際の引数を組み合わせて、フレームの実行状態を作成します。
概念的には:```text
function object
code object
globals
call arguments
x = 9
new frame
code = square.__code__
globals = square.__globals__
locals slot 0 = 9
value stack = empty
instruction pointer = start
```評価ループは、フレームが戻るか、レイズするか、放棄するか、一時停止するまでフレームを実行します。
## 28.4 モジュールフレーム
モジュール本体もフレーム内で実行されます。
ファイルの場合:```python
# app.py
x = 10
def f():
return x
```CPython はモジュール全体をコード オブジェクトにコンパイルします。ファイルを実行すると、そのコード オブジェクトがモジュール名前空間で実行されます。
概念的には:```text
module frame
code = compiled app.py
globals = module.__dict__
locals = module.__dict__
```モジュール スコープでは、グローバルとローカルは通常、同じディクショナリを参照します。
これが、トップレベルの代入がモジュール名前空間に書き込まれる理由です。```python
x = 10
```作成します:```text
module.__dict__["x"] = 10
```関数の実行が異なります。関数ローカルは、モジュール ディクショナリをプライマリ ストレージとしてではなく、高速ローカル スロットを使用します。
## 28.5クラスのボディフレーム
クラス本体もコードとして実行されます。```python
class User:
kind = "human"
def name(self):
return "anonymous"
```class ステートメントは単に静的構造を宣言するだけではありません。 CPython は、一時的な名前空間でクラス本体を実行し、その名前空間からクラス オブジェクトを構築します。
概念的には:```text
prepare class namespace
execute class body frame
collect assignments and function objects
call metaclass to create class object
bind class object to name User
```クラス本体の実行中:```python
kind = "human"
```クラス名前空間に格納されます。
ネストされた関数:```python
def name(self):
return "anonymous"
```関数オブジェクトを作成し、それをクラス名前空間に格納します。メソッド本体は実行されません。
クラス本体が終了すると、CPython は通常、名前空間をメタクラスに渡します。`type`。
## 28.6 フレームの内容
CPython フレームに似た実行レコードには、いくつかのカテゴリのデータが含まれています。
|カテゴリー |目的 |
|---|---|
|コードリファレンス |実行されるコード オブジェクト |
|グローバル |名前検索のためのグローバル名前空間 |
|ビルトイン |組み込みの名前空間フォールバック |
|地元の人 |ローカル変数または名前空間 |
|速いローカル |関数ローカルのスロット配列 |
|値スタック |バイトコードの一時オペランド |
|命令状態 |現在のバイトコード位置 |
|例外状態 |アクティブな例外処理メタデータ |
|発信者関係 |前のフレームへのリンクまたはコンテキストの呼び出し |
|トレース状態 |デバッガー、プロファイラー、カバレッジフック |
|所有者の状態 |ジェネレーター、コルーチン、または通常の呼び出し状態 |
単純化した構造は次のようになります。```text
frame
code object
previous frame
globals dictionary
builtins dictionary
locals representation
fast local slots
value stack
instruction pointer
exception state
tracing flags
```正確な C 構造体は CPython のバージョンによって異なります。概念的フィールドは安定したままです。
## 28.7 高速ローカル
関数ローカルが最適化されます。
この関数では次のようになります。```python
def f(a, b):
c = a + b
return c
```コンパイラはローカル名を認識しています。```text
a
b
c
```それらにインデックスを割り当てます。
|名前 |スロット |
|---|---:|
|`a`| 0 |
|`b`| 1 |
|`c`| 2 |
実行中、CPython はスロットによってローカルにアクセスできます。```text
LOAD_FAST 0
LOAD_FAST 1
STORE_FAST 2
LOAD_FAST 2
```これにより、通常の関数ローカルの辞書検索が回避されます。
フレームは、これらの値を配列のような領域に格納します。```text
fast locals
slot 0: a
slot 1: b
slot 2: c
```これが、ローカル変数がグローバル変数よりも高速である理由の 1 つです。
## 28.8 地元の辞書
Python はローカルを公開します`locals()`そしてフレーム属性。```python
def f(a):
b = a + 1
print(locals())
f(10)
```出力:```text
{'a': 10, 'b': 11}
```ただし、関数内部では、実際の実行ストレージは高速ローカルです。ディクショナリ ビューは、コンテキストと Python のバージョンに応じて実体化されるか、内部ストレージと同期される場合があります。
この区別は重要です。```python
def f():
x = 1
locals()["x"] = 2
return x
```によって返される辞書の変更に依存しないでください。`locals()`関数内の最適化されたローカル変数を変更します。
モジュールスコープでは状況が異なります。```python
locals()["x"] = 2
print(x)
```モジュール レベルでは、ローカルはモジュール ディクショナリであるため、これは通常は機能します。
## 28.9 グローバルとビルトイン
フレームには、名前解決に使用されるグローバル名前空間と組み込み名前空間への参照が保存されます。
のために:```python
def f(xs):
return len(xs)
lenはローカル変数ではありません。 CPython はグローバル ルックアップを使用します。text look in function globals if missing, look in builtins if missing, raise NameError フレームは両方の辞書を提供します。text frame.globals = module namespace frame.builtins = builtins namespace 概念的には:python def f(xs): return len(xs) 次のように実行されます:```text
LOAD_GLOBAL len
LOAD_FAST xs
CALL 1
RETURN_VALUE
`LOAD_GLOBAL`フレームのグローバルと組み込みに依存します。
## 28.10 値スタック
各フレームには、バイトコードの実行で使用される値スタックがあります。
のために:```python
def f(a, b, c):
return (a + b) * c
```スタックはおおよそ次のように変化します。
|指示 |前にスタック | | の後にスタック
|---|---|---|
|`LOAD_FAST a` | `[]` | `[a]` |
| `LOAD_FAST b` | `[a]` | `[a, b]` |
| `BINARY_OP +` | `[a, b]` | `[a + b]` |
| `LOAD_FAST c` | `[a + b]` | `[a + b, c]` |
| `BINARY_OP *` | `[a + b, c]` | `[(a + b) * c]` |
| `RETURN_VALUE` | `[result]`|戻る |
スタックには、ボックス化されていない生の値ではなく、オブジェクト参照が格納されます。
したがって、整数の場合、スタックは Python 整数オブジェクトへのポインターを保持します。```text
value stack
PyObject* -> int object
PyObject* -> int object
```バイトコード命令は、これらのスタック エントリをプッシュ、ポップ、置換、または検査します。
## 28.11 命令位置
フレームは現在のバイトコード位置を追跡します。
直線コードの場合、命令ポインタが進みます。
枝の場合はジャンプします。```python
def abs_like(x):
if x < 0:
return -x
return x
```概念的な制御フロー:```text
load x
load 0
compare <
jump if false to return_x
load x
unary negative
return
return_x:
load x
return
```フレームは次の命令が記録されます。この立場は以下にとって重要です。```text
normal execution
branches
loops
exceptions
tracebacks
line tracing
profiling
debugging
```トレースバックはフレーム状態を使用して、例外が発生した場所を報告します。
## 28.12 フレームとトレースバック
例外が伝播すると、CPython はフレームからのトレースバック情報を記録します。
例:```python
def a():
b()
def b():
c()
def c():
1 / 0
a()
```トレースバックは、アクティブな呼び出しのチェーンを示します。```text
a
b
c
ZeroDivisionError
```各トレースバック エントリは、フレームと命令の位置を参照します。これが、Python がソース ファイル名、行番号、関数名を表示できる理由です。
トレースバックは単なる文字列ではありません。これは、実行時情報の構造化されたチェーンです。
## 28.13 Python からのフレームへのアクセス
Python は、いくつかの API を通じてフレーム オブジェクトを公開します。```python
import inspect
def f():
frame = inspect.currentframe()
print(frame.f_code.co_name)
print(frame.f_locals)
print(frame.f_globals is globals())
f()
```の`frame`オブジェクトは次のようなフィールドを公開します。
|属性 |意味 |
|---|---|
|`f_code`|コードオブジェクト |
|`f_locals`|ローカル名前空間ビュー |
|`f_globals`|グローバル名前空間 |
|`f_builtins`|ビルトイン名前空間 |
|`f_back`|前のフレーム |
|`f_lineno`|電流ソース行 |
|`f_trace`|トレース機能 |
次のものも使用できます。```python
import sys
frame = sys._getframe()
print(frame.f_code.co_name)
```これらの API は強力ですが、実装に依存します。フレーム オブジェクトを維持すると、ローカル変数とオブジェクトも維持できます。
## 28.14 フレームの寿命
通常の関数フレームのほとんどは存続期間が短くなります。```python
def f():
x = object()
return 1
f()
```いつ`f`が返された場合、そのフレームは破棄または再利用でき、ローカル参照は解放できます。
ただし、次のような場合には、フレームの存続期間が長くなることがあります。```text
tracebacks keep frames alive
generators suspend frames
coroutines suspend frames
debuggers inspect frames
profilers observe frames
closures may keep cells alive
manual references to frames keep them alive
```例:```python
import inspect
saved = None
def f():
global saved
x = [1, 2, 3]
saved = inspect.currentframe()
f()
```グローバルな`saved`フレームを指します。そのフレームはその地元の人々を指します。に割り当てられたリスト`x`フレームが生きたままであるため、生きたままになる可能性があります。
これは、デバッグ ツールや例外処理コードにおける驚くべきメモリ保持の一般的な原因です。
## 28.15 フレームチェーン
フレームは呼び出し元を次のように参照できます。`f_back`。```python
import inspect
def outer():
inner()
def inner():
frame = inspect.currentframe()
print(frame.f_code.co_name)
print(frame.f_back.f_code.co_name)
outer()
```概念的には:```text
inner frame
f_back -> outer frame
f_back -> module frame
```このチェーンにより、トレースバック構築とスタック検査が可能になります。
また、最も内側のフレームを保持すると、呼び出し元のフレームを維持できることも意味します。```text
saved inner frame
keeps outer frame
keeps module frame references
```メモリに依存するツールの場合、フレーム参照を意図的に解放する必要があります。
## 28.16 ジェネレーターとサスペンドフレーム
ジェネレーターは、譲歩した後も実行状態を保持します。```python
def gen():
x = 1
yield x
x = 2
yield x
```関数を呼び出すと、ジェネレーター オブジェクトが作成されます。```python
g = gen()
```体はすぐには動きません。再開時:```python
next(g)
```フレームは最初のフレームまで実行されます`yield`。
後`yield`、フレームは中断されたままになります。```text
generator object
suspended frame
code object
local x = 1
instruction position after first yield
value stack state
```次の呼び出しは、保存された命令位置から再開されます。```python
next(g)
```これは、リターン時に終了する通常の関数フレームとは異なります。
## 28.17 コルーチンとフレーム
コルーチンは、ジェネレーターと同じ一般的な考え方、つまり再開可能な実行状態を使用します。```python
async def fetch():
data = await read()
return data
```で`await`、コルーチンは一時停止できます。そのフレームには以下が保持されます。```text
local variables
current instruction position
pending awaitable
exception state
return path
```イベント ループによって後で再開されます。```text
coroutine object
suspended frame or frame-like state
locals
stack
instruction pointer
```したがって、非同期実行はフレームの一時停止と再開に依存します。これは別個の言語ランタイムではありません。
## 28.18 フレームの実体化
最新の CPython は、内部実行フレームを Python に表示される完全なフレーム オブジェクトから区別します。
インタプリタは、パフォーマンスのためにコンパクトな内部フレームで実行できます。フルフレーム オブジェクトは、イントロスペクション、トレース、デバッグ、トレースバック処理、または API などで必要な場合にのみ作成できます。`sys._getframe()`。
概念的には:```text
internal frame
optimized execution record
Python frame object
object exposed to Python code
wraps or materializes execution state
```すべての関数呼び出しで、ユーザー コードから見えるヒープ割り当ての Python フレーム オブジェクトが必要なわけではないため、この区別によりパフォーマンスが向上します。
概念モデルは同じままです。つまり、実行状態がまだ存在します。正確な表現は異なる場合があります。
## 28.19 フレームとクロージャー
クロージャには、入れ子になった関数間で共有される変数用の特別なストレージが必要です。
例:```python
def outer():
x = 10
def inner():
return x
return inner
```変数`x`その後も生き残らなければならない`outer`返品するため`inner`まだ使っています。
CPython はセル オブジェクトを使用してこれを処理します。
概念的には:```text
outer frame
x stored in cell
inner function
closure references same cell
```後`outer`が返されると、フレームは消える可能性がありますが、返された関数がセルを参照しているため、セルは生きたままになります。```python
fn = outer()
print(fn())
```クロージャーは全体を保持しません`outer`通常の場合、フレームは生きています。必要なセル変数を維持します。
## 28.20 セル変数と自由変数
コンパイラはクロージャ変数を分類します。
|用語 |意味 |
|---|---|
|セル変数 |内部関数によってキャプチャされたローカル変数 |
|自由変数 |ここで使用されている変数ですが、外側のスコープで定義されています。
例:```python
def outer():
x = 1
def inner():
return x
return inner
```のために`outer`、`x`はセル変数です。
のために`inner`、`x`は自由変数です。
これを検査できます:```python
def outer():
x = 1
def inner():
return x
return inner
print(outer.__code__.co_cellvars)
print(outer().__code__.co_freevars)
```フレーム レイアウトにはこれらのセル用のストレージが含まれているため、入れ子になった関数が変数を安全に共有できます。
## 28.21 フレームと例外
フレームには例外処理状態が保存されます。
のために:```python
def f(x):
try:
return 10 / x
except ZeroDivisionError:
return 0
```コード オブジェクトには例外処理メタデータが含まれています。フレームには、そのメタデータを使用するために必要な現在の実行状態が保存されます。
除算で例外が発生すると、インタプリタはフレームの命令位置を使用して一致するハンドラを見つけます。
概念的には:```text
exception raised
inspect current frame
locate handler for current bytecode range
adjust stack state
jump to handler
```現在のフレームにハンドラーが存在しない場合、フレームは巻き戻され、例外は呼び出し側フレームに伝播されます。
## 28.22 フレームと`finally`あ`finally`ブロックは正確なフレーム状態にも依存します。```python
def f():
try:
return 1
finally:
cleanup()
```の`finally`関数が戻ってもブロックは実行されます。
フレームは、クリーンアップ コードの実行中にリターンが保留されていることを覚えておく必要があります。
概念的には:```text
start return with value 1
enter finally block
call cleanup
if cleanup succeeds:
complete original return
if cleanup raises:
replace return with new exception
```これが、例外と戻り状態が単純な後付けではなく、フレーム実行の一部である理由です。
## 28.23 フレームとトレース
トレースフックはフレーム上で動作します。```python
import sys
def trace(frame, event, arg):
print(event, frame.f_code.co_name, frame.f_lineno)
return trace
def f(x):
y = x + 1
return y
sys.settrace(trace)
f(10)
sys.settrace(None)
```トレース関数は現在のフレームを受信します。コード、ローカル、グローバル、行番号、呼び出し関係を検査できます。
トレースは次の場合に役立ちます。```text
debuggers
coverage tools
teaching tools
profilers
runtime monitors
```しかし、追跡にはコストがかかります。これにより、インタプリタはより多くの実行ポイントでより多くの状態を保持し、公開するようになります。
## 28.24 フレームとプロファイリング
プロファイリングは似ていますが、通常はトレースよりも粗いです。```python
import sys
def profile(frame, event, arg):
print(event, frame.f_code.co_name)
sys.setprofile(profile)
def f():
return 1
f()
sys.setprofile(None)
```プロファイリング イベントには呼び出しと戻りが含まれます。プロファイラーはフレーム データを使用して、関数の時間や呼び出し回数を特定します。
フレームは、ランタイム実行からソースレベルのプログラム構造へのマッピングを提供します。
## 28.25 フレームと再帰
再帰呼び出しでは、同じコード オブジェクトに対して複数のフレームが作成されます。```python
def fact(n):
if n <= 1:
return 1
return n * fact(n - 1)
```のために`fact(4)`:
```text
fact(4)
fact(3)
fact(2)
fact(1)
```各通話には独自のローカルがあります`n`。```text
frame fact: n = 4
frame fact: n = 3
frame fact: n = 2
frame fact: n = 1
```すべてのフレームは同じコード オブジェクトを参照しますが、ローカル スロットは異なります。
CPython は再帰の深さを追跡して、無制限の再帰呼び出しが下位レベルのリソースを使い果たさないようにします。
## 28.26 フレームとメモリ保持
フレームは予想よりも多くのメモリを保持できます。
例:```python
def load_big():
data = bytearray(100_000_000)
raise RuntimeError("failed")
try:
load_big()
except RuntimeError as exc:
saved = exc
```例外はトレースバックを保持できます。トレースバックはフレームを保持できます。フレームはローカル変数を保持できます。したがって`data`生き続けるかもしれない。
一般的なクリーンアップ パターンは次のとおりです。```python
try:
load_big()
except RuntimeError as exc:
handle(exc)
exc = None
```または、例外を必要以上に長く保存しないようにします。
保持チェーンは次のようになります。```text
exception
traceback
frame
locals
large object
```フレームを理解すると、この動作を説明するのに役立ちます。
## 28.27 フレームオブジェクトは内省的であり、自由ではありません
フレームのイントロスペクションは強力ですが、コストがかかります。
次のような操作により、フレーム オブジェクトの作成または同期が強制される可能性があります。```python
inspect.currentframe()
sys._getframe()
locals()
traceback inspection
debugger hooks
coverage tracing
```これは以下に影響を与える可能性があります。```text
performance
memory lifetime
local variable synchronization
optimizer freedom
debugging visibility
```通常のアプリケーション コードの場合は、ホット パスでのフレーム イントロスペクションを避けてください。デバッガー、プロファイラー、およびランタイム ツールにとって、フレーム イントロスペクションは不可欠です。
## 28.28 簡略化されたフレーム実行
簡略化された関数実行モデル:```c
PyObject *
run_function(PyFunctionObject *func, PyObject **args, int nargs)
{
Frame frame;
frame.code = func->code;
frame.globals = func->globals;
frame.builtins = get_builtins(func->globals);
frame.localsplus[0] = args[0];
frame.localsplus[1] = args[1];
frame.stack_pointer = frame.stack;
frame.instruction_pointer = frame.code->first_instruction;
return eval_frame(&frame);
}
```これでは多くの実際の詳細が省略されています。```text
keyword arguments
defaults
closures
cell variables
free variables
generators
coroutines
exceptions
tracing
profiling
reference ownership
specialization
thread state
recursion checks
```ただし、形状は正確です。関数呼び出しによってフレームが準備され、評価ループによってそれが実行されます。
## 28.29 簡略化されたフレームレイアウト
指導レイアウト:```c
typedef struct {
CodeObject *code;
DictObject *globals;
DictObject *builtins;
Object **localsplus;
Object **stack_pointer;
Instruction *instruction_pointer;
ExceptionState exception_state;
struct Frame *previous;
} Frame;
```重要な考え方は、効率的な実行のために、ローカル スロットとスタック値が互いに近くに格納されることが多いということです。
概念的には:```text
frame memory
fixed metadata
locals and cells
value stack
```コンパイラは、必要なローカル スロットとスタック スロットの数を計算します。
## 28.30 ローカルズプラスエリア
CPython は、実行フレーム内のローカルとスタックのようなデータに結合された領域を使用します。
概念的なレイアウト:```text
localsplus
fast locals
cell variables
free variables
value stack
```のために:```python
def f(a, b):
c = a + b
return c
```レイアウトは次のように理解できます。```text
localsplus
[0] a
[1] b
[2] c
[3...] value stack area
```クロージャの場合、セルと自由変数もコード オブジェクトに既知の位置を占めます。
このコンパクトなレイアウトにより、割り当てのオーバーヘッドが削減され、局所性が向上します。
## 28.31 フレーム状態の遷移
フレームはいくつかの状態を通過できます。```text
created
↓
executing
↓
returned
```ジェネレーターとコルーチンはさらに状態を追加します。```text
created
↓
suspended
↓
executing
↓
suspended
↓
completed
```例外パス:```text
executing
↓
exception raised
↓
handler found
↓
executing handler
```または:```text
executing
↓
exception raised
↓
no handler
↓
unwound
```フレームの状態によって、どの操作が正当であるかが決まります。完了したジェネレーターは再開できません。稼働中の発電機に再び入ることはできません。
## 28.32 リエントラント実行
Python の実行は再入可能です。
フレームは、最初の命令が終了する前に別のフレームを作成するユーザー コードを呼び出す命令を実行する場合があります。
例:```python
class X:
def __add__(self, other):
return 42
def f(a, b):
return a + b
f(X(), X())
```の`BINARY_OP`での指導`f`電話`X.__add__`、別の Python フレームを実行します。
概念的には:```text
frame f
BINARY_OP
calls __add__
frame X.__add__
return 42
continue frame f
```このため、CPython の評価モデルは、Python に再入力される可能性のある C ヘルパー呼び出し全体でフレーム状態を保持する必要があります。
## 28.33 フレームと C スタック
Python フレームと C スタックは関連していますが、別個のものです。
Python 関数呼び出しにより、Python 実行状態が作成されます。 CPython のバージョンと呼び出しパスによっては、C スタックも増大する可能性があります。
インタプリタは時間をかけて Python 呼び出しにおける不必要な C 再帰を減らすよう取り組んできましたが、ネイティブ呼び出し、拡張呼び出し、および一部のランタイム パスには依然として C スタックが関与しています。
重要な違い:```text
Python frame
Python-level execution state
C stack frame
native machine call state
```Python トレースバックには、インタープリター内のすべての C スタック フレームではなく、Python フレームが表示されます。
## 28.34 フレームクリア
フレームをクリアして参照を解放できます。
フレームは多くのオブジェクトを参照します。```text
locals
globals
builtins
stack values
trace function
exception state
previous frame
```フレームが不要になった場合、CPython はオブジェクトを収集できるように、所有されている参照を解放する必要があります。
ジェネレーターとコルーチンの場合、フレームが一時停止される可能性があるため、クリアはよりデリケートになります。ジェネレーターを閉じると、そのフレーム状態が安全に解放される必要があります。
例:```python
def gen():
data = bytearray(100_000_000)
yield 1
g = gen()
next(g)
del g
```ジェネレーターを削除すると、一時停止されたフレームが解放されるため、解放される可能性があります。`data`、他に参照が存在しないと仮定します。
## 28.35 フレーム検査の例
このプログラムは、単純な呼び出しスタックを出力します。```python
import sys
def print_stack():
frame = sys._getframe()
while frame is not None:
print(frame.f_code.co_name, frame.f_lineno)
frame = frame.f_back
def c():
print_stack()
def b():
c()
def a():
b()
a()
```概念的には、チェーンは次のとおりです。```text
print_stack
c
b
a
<module>
```正確な行番号はファイルによって異なります。
この例では、フレームが Python コードから見えるリンクされたランタイム スタックをどのように形成するかを示します。
## 28.36 フレーム設計のトレードオフ
フレームはパフォーマンスと内省の間に位置します。
CPython は、すべての Python 呼び出しでフレームを使用するため、フレームの高速化を望んでいます。ただし、Python はフレームをユーザー コードやツールにも公開します。
これにより緊張が生じます。
|目標 |圧力 |
|---|---|
|高速通話 |フレームをコンパクトかつ内部に保つ |
|デバッグ |リッチフレームオブジェクトを公開する |
|プロファイリング |通話と回線のメタデータを保持する |
|トレースバック |障害後も十分な状態を維持 |
|発電機 |サポートサスペンション |
|コルーチン |非同期一時停止をサポート |
|メモリ効率 |不要な参照を保持しないようにする |
|互換性 | Python レベルのフレーム API を保持する |
CPython フレームワークの多くは、これらの制約のバランスを取ることを目的としています。
## 28.37 よくある誤解
|誤解 |正しいモデル |
|---|---|
|フレームはコードオブジェクトと同じです |フレームはコード オブジェクトを実行します。
|すべての関数オブジェクトには 1 つのフレームがあります。すべてのアクティブな呼び出しには個別のフレーム状態があります。
|ローカル変数は常に dict 内に存在します。関数ローカルは通常、高速スロットを使用します。
|`locals()`は常に実ストレージです |関数では、ビューまたは同期マッピングの場合があります。
|トレースバックは単なる文字列です。トレースバック参照フレームとコード位置 |
|ジェネレーターは毎回再起動します。ジェネレーターは一時停止されたフレームを再開します。
|クロージャーは外側のフレーム全体を生かします |通常、それらは必要な細胞を生かし続けます。
|フレームのイントロスペクションは無料です |パフォーマンスとメモリの寿命に影響を与える可能性があります。
## 28.38 実践的な読書戦略
CPython のフレームを学習するには、Python レベルの動作から始めます。
使用:```python
import dis
import inspect
import sys
```この関数を学習してください。```python
def outer(x):
y = x + 1
def inner(z):
return x + y + z
return inner
```検査:```python
fn = outer(10)
print(outer.__code__.co_varnames)
print(outer.__code__.co_cellvars)
print(fn.__code__.co_freevars)
print(fn.__closure__)
dis.dis(outer)
dis.dis(fn)
```次に、出力を次の概念にマッピングします。```text
fast locals
cell variables
free variables
code objects
function objects
frames
stack effects
closure cells
```これにより、Python 構文からフレーム内部への具体的なルートが得られます。
## 28.39 章の概要
フレームは、Python コードの実行ブロックに対する CPython の実行記録です。コード オブジェクトをライブの実行時状態 (ローカル、グローバル、組み込み、スタック値、命令の位置、例外状態、呼び出し元のコンテキスト) にバインドします。
フレームでは、関数呼び出し、モジュール実行、クラス本体、トレースバック、デバッグ、プロファイリング、再帰、ジェネレーター、コルーチン、クロージャー、およびメモリー保持について説明します。
主なモデルは次のとおりです。```text
code object
immutable instructions and metadata
frame
mutable execution state for one execution
evaluation loop
runs the frame until return, exception, yield, or suspension
```フレームを理解すると、評価ループが具体的になります。インタプリタは抽象ソース コードを実行していません。バイトコード命令を通じてフレーム状態を進めます。