5. ランタイムモデル
#5. ランタイムモデル
CPython ランタイムは、プロセスの開始後、Python コードの実行が終了するまでに存在する機構です。インタプリタ状態、スレッド状態、モジュール、ビルトイン、メモリ アロケータ、例外状態、インポート状態、フレーム、保留中の呼び出し、シグナル処理、およびシャットダウン動作を所有します。
Python プログラムは一連のステートメントとして実行されるように見えます。 CPython は、階層化されたランタイム システム内でそれを実行します。```text operating system process CPython runtime interpreter state thread state frame stack executing code object bytecode instructions object operations
## 5.1 プロセス、ランタイム、インタプリタ、スレッド、フレーム
実行中の CPython プログラムには、複数のネストされた実行ユニットがあります。
|単位 |意味 |
| ------------ | -------------------------------------------------------------------- |
|プロセス | CPython 実行可能ファイルまたは埋め込みランタイムを含む OS プロセス |
|ランタイム |プロセス全体で共有されるグローバル CPython 状態 |
|通訳 |ランタイム内の分離された Python インタープリターの状態 |
|スレッドの状態 | 1 つのインタープリターのスレッドごとの実行状態 |
|フレーム | 1 つのアクティブな実行コンテキスト |
|コードオブジェクト |コンパイルされたバイトコードとメタデータ |
|オブジェクト |バイトコードによって操作されるランタイム値 |
簡単なスクリプト:```python
x = 1
print(x + 2)
```フレーム内を走ります。そのフレームはスレッド状態に属します。スレッドの状態はインタープリタに属します。インタプリタは CPython ランタイムに属します。
概念的には:```text
_PyRuntimeState
PyInterpreterState
PyThreadState
PyFrameObject or internal frame
PyCodeObject
locals
globals
builtins
value stack
```正確な C 構造体はバージョンによって異なりますが、階層はソースの読み取りをガイドするのに十分安定しています。
## 5.2 ランタイム
ランタイムはプロセス全体の CPython 状態です。
これには、1 つ以上のインタープリターによって使用されるグローバル サービスが含まれます。```text
runtime initialization state
memory allocator state
interpreter list
GIL state
pending calls
signal handling state
audit hook state
global caches
runtime finalization flags
```大まかに言うと、ランタイムは次のように答えます。```text
Has CPython been initialized?
Which interpreters exist?
Is the runtime finalizing?
Which global locks and services are active?
Which process-wide hooks are installed?
```これは、起動、シャットダウン、埋め込み、サブインタープリタ、およびフリースレッド作業中に最も重要です。
通常の Python スクリプトには、通常、1 つのランタイムと 1 つのメイン インタプリタがあります。
## 5.3 インタプリタの状態
インタープリターの状態は、プロセス内の 1 つの Python インタープリターを表します。
次のような言語レベルの状態を所有します。```text
modules dictionary
builtins
import state
sys module state
codec state
warnings state
garbage collector state
interned strings
per-interpreter caches
execution configuration
```概念的には:```text
PyInterpreterState
modules
builtins
sysdict
import machinery
gc generations
codec registry
pending async exception state
```ほとんどの Python プログラムはメイン インタプリタのみを使用します。
サブインタープリタは、同じプロセス内に追加のインタープリタ状態を作成します。これらは個別のモジュール ディクショナリと実行時状態を持つことができますが、それでも一部のプロセス レベルのリソースを共有します。
## 5.4 スレッドの状態
Python コードを実行する各 OS スレッドにはスレッド状態が必要です。
スレッド状態は、1 つのインタープリター内の 1 つのスレッドの実行情報を保存します。```text
current frame
current exception state
recursion depth
profiling function
tracing function
async exception request
thread-local interpreter data
```スレッド状態は、ネイティブ実行を Python 実行に接続します。
概念的には:```text
current OS thread
PyThreadState
current interpreter
current frame
exception information
tracing and profiling hooks
```C コードで例外を発生させたり、現在のフレームにアクセスしたり、Python API と対話したりする必要がある場合、多くの場合、現在のスレッドの状態が必要になります。
## 5.5 フレーム
フレームは実行記録です。
CPython は、モジュール本体、関数本体、クラス本体、ジェネレーター、コルーチン、または内包表記を実行するときにフレームを作成します。
フレームには次の内容が含まれます。```text
code object
globals dictionary
builtins dictionary
locals storage
value stack
instruction pointer
block and exception state
line number state
owner information
```関数の場合:```python
def add(a, b):
c = a + b
return c
```電話をかける`add(2, 3)`その呼び出しのフレームを作成します。
フレームには以下が格納されます。```text
code object for add
a = 2
b = 3
c after assignment
temporary stack values
current bytecode offset
globals of the defining module
builtins visible to the function
```フレームは、関数呼び出しをアクティブにする具体的なランタイム オブジェクトです。
## 5.6 コードオブジェクト
コード オブジェクトは、コンパイルされた実行可能コンテンツです。
これは不変のメタデータとバイトコードです。ローカル変数の現在値は保存されません。
例えば:```python
def f(x):
y = x + 1
return y
```関数オブジェクトにはコード オブジェクトがあります。```python
code = f.__code__
print(code.co_name)
print(code.co_varnames)
print(code.co_consts)
```コード オブジェクトは、関数の実行方法を示します。フレームには、そのコードの 1 つのアクティブな実行が保存されます。
この区別は重要です。```text
code object: reusable compiled program
frame: one running invocation of that program
function: callable object that wraps code with globals, defaults, and closure
```同じ関数への呼び出しの多くは同じコード オブジェクトを再利用しますが、別のフレームが作成されます。
## 5.7 関数オブジェクト
Python 関数オブジェクトは、コード オブジェクトを実行時コンテキストでラップします。
これには以下が含まれます:```text
code object
globals dictionary
defaults
keyword-only defaults
closure cells
annotations
function name
qualified name
module name
dict for custom attributes
```例えば:```python
x = 10
def f(y):
return x + y
```機能`f`バイトコード以上のものが必要です。グローバル辞書も必要です。`x`見つけることができます。
概念的には:```text
PyFunctionObject
func_code code for f
func_globals module globals
func_defaults default argument values
func_closure captured cells
```関数を呼び出すと、これらのコンポーネントを使用してフレームが作成されます。
## 5.8 オブジェクトと型
実行時に、バイトコードはオブジェクト参照を操作します。
すべての Python 値はオブジェクトです。```python
42
"hello"
[1, 2, 3]
{"a": 1}
lambda x: x + 1
Exception("bad")
```各オブジェクトにはタイプがあります。
タイプによって動作が決まります。```text
allocation
deallocation
attribute lookup
method lookup
call behavior
numeric operations
sequence operations
mapping operations
iteration
representation
hashing
comparison
```例えば:```python
len(x)
```考えられるすべてのオブジェクト レイアウトを直接検査するわけではありません。オブジェクトのタイプに長さがどのように機能するかを尋ねます。
これが、ランタイム モデルが型オブジェクトに依存する理由です。バイトコード命令は汎用です。タイプ スロットは具体的な動作を提供します。
## 5.9 名前空間
Python の実行では名前空間が使用されます。
主な名前空間のカテゴリは次のとおりです。
|ネームスペース |バッキングストレージ |
| ----------------- | -------------------------------------------------- |
|地元の人 |関数の高速ローカル、クラス名前空間、またはマッピング |
|グローバル |モジュール辞書 |
|ビルトイン |ビルトイン辞書 |
|オブジェクトの属性 |インスタンス辞書、スロット、記述子、型検索 |
|モジュールの属性 |モジュール辞書 |
このコードの場合:```python
x = 1
def f():
return x + len([1, 2])
```内部`f`, CPython は次のように解決します。```text
x global name in module dictionary
len builtin name if not found in globals
[1,2] newly created list object
```名前解決は、コンパイル時の分類と実行時の名前空間検索に依存します。
## 5.10 高速ローカル
関数のローカル変数は通常、アクセスごとに通常の辞書検索を行うのではなく、最適化された配列のようなレイアウトで保存されます。
関数の場合:```python
def f(a, b):
c = a + b
return c
```コンパイラはローカル名を認識しています。```text
a
b
c
```フレームはそれらをインデックス付きスロットに保存できます。
これにより、バイトコードは高速なローカル操作を使用できるようになります。```text
LOAD_FAST a
LOAD_FAST b
STORE_FAST c
```これは辞書検索よりも高速です。
ローカル辞書は、必要に応じて、たとえば次のように具体化できます。`locals()`、トレース、デバッグ、またはフレーム検査。ただし、通常の実行パスでは高速ローカルが使用されます。
## 5.11 値スタック
CPython バイトコードはスタック マシンを使用します。
命令は、フレームの値スタックから一時的な値をプッシュおよびポップします。
のために:```python
z = x + y
```概念的な実行は次のとおりです。```text
LOAD_FAST x push x
LOAD_FAST y push y
BINARY_OP + pop x and y, push result
STORE_FAST z pop result into local z
```値スタックは一時的な実行ストレージです。ローカル変数とは別のものです。
したがって、フレームには次の両方が含まれます。```text
local variable storage
value stack for intermediate operations
```これは、バイトコードをコンパクトにできる理由を説明します。命令はスタックを通じて通信します。
## 5.12 呼び出し
関数呼び出しは、最も重要なランタイム操作の 1 つです。
のために:```python
result = f(1, 2)
```CPython は次のことを行う必要があります。```text
evaluate f
evaluate arguments
prepare call layout
check callable type
create or enter callable execution
bind arguments
execute body or C function
return result
```呼び出し可能なオブジェクトが異なれば、パスも異なります。
|呼び出し可能 |ランタイムパス |
| ---------------------- | ------------------------------------------ |
| Python関数 |フレームを作成してコードオブジェクトを実行する |
|組み込み関数 | C 関数ラッパーの呼び出し |
|方法 |レシーバーをバインドして基になる関数を呼び出す |
|クラス |インスタンスの割り当てと初期化 |
|オブジェクトと`__call__`|呼び出しタイプ呼び出しスロット |
CPython は、一時的なタプルと辞書の割り当てを減らすために呼び出し規則を最適化しました。最新の CPython は、多くの呼び出し可能な型に対して、vectorcall などの高速呼び出しパスを使用します。
## 5.13 例外
例外は、実行時の状態と制御フローです。
Python コードが発生する場合:```python
raise ValueError("bad")
```CPython は、現在のスレッド状態の例外情報を記録し、アンワインド実行を開始します。
C 関数は通常、次の方法で失敗を報告します。```text
setting an exception
returning NULL or -1
```呼び出し元は戻り値をチェックし、失敗を伝播します。
概念的には:```c
PyErr_SetString(PyExc_ValueError, "bad");
return NULL;
```バイトコード レベルでは、例外は以下に影響します。```text
current frame
exception table
stack unwinding
finally blocks
except matching
traceback construction
propagation to caller
```例外は通常の戻り値ではありません。これらは、ランタイムを通る別の制御パスです。
## 5.14 トレースバック
トレースバックは、例外がどこに到達したかを記録します。
例外がフレームを介して伝播する場合、CPython は以下を識別するトレースバック エントリを添付できます。```text
file name
function name
line number
bytecode position
frame
```例えば:```python
def a():
b()
def b():
1 / 0
a()
```トレースバックには呼び出しチェーンが含まれています。```text
module frame
a frame
b frame
```トレースバックはオブジェクトです。 Holding a traceback can keep frames alive. Holding frames can keep local variables alive. This is important for memory behavior.
## 5.15 モジュール
A module is an object with a namespace.
CPython がモジュールをインポートすると、モジュール オブジェクトが作成または取得され、そのモジュールのディクショナリ内のコードが実行されます。
概念的には:```text
find module spec
create module object
insert into sys.modules
execute module code in module namespace
return module object
```モジュール ディクショナリは、そのモジュールで定義された関数のグローバル名前空間になります。
のために:```python
# example.py
x = 10
def f():
return x
```機能`f`モジュールのグローバル辞書への参照を保存します。いつ`f`見上げる`x`、その辞書を検索します。
##5.16`sys.modules`
`sys.modules`インポートキャッシュです。
モジュール名をモジュール オブジェクトにマップします。```python
import sys
print(sys.modules["sys"])
```このキャッシュにより、インポートが繰り返されて同じモジュールが再実行されるのを防ぎます。
同じモジュールを 2 回インポートすると、通常はすでにロードされているモジュールが返されます。```python
import math
import math
```2回目のインポートチェック`sys.modules`そしてモジュールを再利用します。
このキャッシュは循環インポートも処理します。モジュールは次の場所に表示される場合があります`sys.modules`コードの実行が完了する前に。
## 5.17 ビルトイン
ビルトインは、ローカルおよびグローバル検索が失敗した場合に使用できる名前です。
例:```python
len
print
range
object
type
Exception
```フレームは組み込み辞書にアクセスできます。
関数内の非修飾名の名前検索は、おおよそ次のようになります。```text
locals
globals
builtins
```それで:```python
def f(xs):
return len(xs)
```通常は解決します`len`グローバルという名前が付いている場合を除き、組み込みから`len`それを影にします。
このルックアップ パスはランタイム モデルの一部です。という名前のグローバルを割り当てる理由を説明します。`len`モジュール内の動作を変更します。
## 5.18 記述子と属性アクセス
属性アクセスはランタイムディスパッチです。
のために:```python
obj.name
```CPython は単に内部を調べるだけではありません`obj.__dict__`。
これは、記述子と型の検索ルールに従います。```text
look on type for data descriptor
look in instance dictionary
look on type for non-data descriptor or class attribute
call __getattr__ if needed
raise AttributeError if missing
```これが、メソッドが自動的にバインドされる理由です。```python
class C:
def f(self):
return 1
c = C()
m = c.f
```クラスに格納される関数オブジェクトはディスクリプタです。インスタンスを通じてアクセスすると、バインドされたメソッドまたは最適化された同等の呼び出しパスが作成されます。
属性ルックアップは、オブジェクト レイアウト、型オブジェクト、記述子、メソッド呼び出し、およびパフォーマンスを結び付けます。
## 5.19 反復
反復では小規模なランタイム プロトコルを使用します。
のために:```python
for x in obj:
body(x)
```CPython は大まかに次のことを行います。```text
iterator = iter(obj)
loop:
x = next(iterator)
if StopIteration: exit loop
execute body
```C レベルでは、これはタイプ スロットとプロトコル ヘルパーにマップされます。
iterator オブジェクトは反復状態を保存します。リスト反復子の場合、その状態にはリストと現在のインデックスが含まれます。ジェネレーターの場合、イテレーターは一時停止された実行フレームそのものです。
この共通プロトコルは以下を実現します。```text
for loops
comprehensions
tuple unpacking
list()
sum()
any()
all()
many standard library functions
```## 5.20 ジェネレーターとコルーチン
ジェネレーターは吊り下げられたフレームです。
のために:```python
def gen():
yield 1
yield 2
```電話をかける`gen()`本体はすぐには実行されません。ジェネレーターオブジェクトを作成します。
ジェネレーターは実行状態を所有します。```text
code object
suspended frame or frame-like state
instruction position
local variables
exception state
running flag
```電話をかける`next()`次まで実行を再開します`yield`または戻る。
コルーチンと非同期ジェネレーターは、待機可能なプロトコルの動作とイベント ループの統合によってこのアイデアを拡張します。
## 5.21 メモリ管理
ランタイムはメモリをいくつかの層で管理します。```text
raw memory allocator
object allocator
type-specific free lists or caches
reference counting
cyclic garbage collector
```参照カウントは、ほとんどのライフタイム イベントを処理します。```text
new reference increases lifetime
decref releases ownership
zero refcount triggers deallocation
```循環ガベージ コレクターは、到達不能なオブジェクトの循環を処理します。
オブジェクトの破棄は、ファイナライザー、weakref コールバック、またはより多くのオブジェクトを解放する割り当て解除パスを通じてコードを実行できるため、メモリ管理は実行時の動作の一部です。
## 5.22 ランタイムモデルの GIL
従来の CPython ランタイムでは、グローバル インタープリター ロックは、Python バイトコードと多くの内部データ構造の実行を保護します。
GIL は以下を簡素化します。```text
reference count updates
object mutation invariants
interpreter state access
C extension assumptions
```Python バイトコードを実行するには、スレッドが GIL を保持する必要があります。
C 拡張機能は、ブロッキングまたは長時間実行されるネイティブ作業に関して GIL を解放し、Python オブジェクトに再び触れる前に GIL を再取得できます。
したがって、ランタイム モデルには 2 つの同時実行レイヤーがあります。```text
OS threads may run concurrently
Python bytecode execution is serialized by the GIL in traditional builds
```最新の CPython にはフリースレッドのビルド作業もあり、多くの内部前提が変更されます。しかし、従来の GIL モデルは、既存のコードと拡張機能を理解するために依然として不可欠です。
## 5.23 初期化
Python コードが実行される前に、CPython はランタイムを初期化します。
スタートアップには次のものが含まれます。```text
configure memory allocators
initialize runtime state
create main interpreter
create main thread state
initialize builtins
initialize sys
set up import machinery
initialize encodings
configure paths
process command-line options
run startup hooks
execute requested code
```起動コードが複雑になるのはこのためです。多くのモジュールは既存の他のモジュールに依存していますが、インポート システム自体にもランタイム サポートが必要です。
CPython は慎重に自身をブートストラップします。
## 5.24 シャットダウン
シャットダウンも複雑です。
ファイナライズには次のことが含まれる場合があります。```text
running atexit handlers
flushing standard streams
clearing modules
destroying interpreter state
collecting garbage
finalizing objects
releasing memory
tearing down runtime services
```オブジェクトはシャットダウン中にファイナライザーを実行できますが、そのモジュール グローバルはすでにクリアされている可能性があります。シャットダウンのバグがわかりにくいのはこのためです。
期待するファイナライザー`sys`、`os`、または完全に利用可能にする別のモジュールがインタープリタのティアダウン中に失敗する可能性があります。
## 5.25 組み込み Python
CPython は、別の C または C++ プログラム内に埋め込むことができます。
その場合、ホスト プロセスは実行時の初期化と終了処理を制御します。
概念的には:```c
Py_Initialize();
/* run Python code */
Py_Finalize();
```埋め込むと、ランタイム階層がより見やすくなります。 CPython ランタイムは、独自のスレッド、アロケーター、イベント ループ、ロギング システム、およびシャットダウン ルールを持つプロセス内に存在します。
埋め込みコードは以下を考慮する必要があります。```text
initialization order
thread state management
GIL rules
reference ownership
exception handling
finalization constraints
```## 5.26 実行時の状態と可観測性
Python は、標準モジュールを通じてランタイムの一部を公開します。
|モジュール |ランタイムビュー |
| ------------- | -------------------------------------------------------- |
|`sys`|インタプリタ設定、モジュール、パス、フレーム、refcounts |
|`gc`|ガベージ コレクターの状態 |
|`inspect`|フレーム、関数、ソース、シグネチャ |
|`dis`|バイトコード |
|`types`|ランタイム型オブジェクト |
|`threading`| Python スレッドの抽象化 |
|`tracemalloc`|割り当てトレース |
|`importlib`|輸入機械 |
例:```python
import sys
import gc
import inspect
print(sys.modules.keys())
print(gc.get_count())
print(inspect.currentframe())
```これらのモジュールを使用すると、C コードをすぐに読み取らなくてもランタイム構造を観察できるため便利です。
## 5.27 完全な実行スケッチ
このプログラムの場合:```python
def add(a, b):
return a + b
print(add(2, 3))
```CPython は大まかに次のことを行います。```text
initialize runtime
create main interpreter
create main thread state
load builtins and sys
compile module source to code object
create module frame
execute module bytecode
define function:
create code object for add
create function object
bind name add in module globals
call print(add(2, 3)):
load print from builtins
load add from globals
load constants 2 and 3
call add
create frame for add
bind a = 2, b = 3
load a
load b
perform binary addition through object protocol
return integer result
call print
execute built-in C function
write output
discard return value
finish module frame
run shutdown sequence
```これは動作中のランタイム モデルです。
## 5.28 働くメンタルモデル
以降の章を読みながら、このコンパクトなモデルを維持してください。```text
A process owns a CPython runtime.
The runtime owns interpreters.
An interpreter owns modules, builtins, import state, and GC state.
A thread state owns the current execution state for one thread.
A frame runs one code object.
Bytecode instructions operate on a value stack and local storage.
Objects carry type pointers.
Types define behavior through slots.
Errors use exception state plus sentinel returns.
Memory is managed by reference counting plus cyclic GC.
```このモデルは、ほとんどの CPython 内部を接続します。
## 5.29 章の概要
CPython ランタイムは階層化された実行システムです。プロセスにはランタイムが含まれています。ランタイムにはインタープリターの状態が含まれます。実行中の各スレッドにはスレッド状態があります。アクティブな通話にはそれぞれフレームがあります。各フレームはコード オブジェクトを実行します。バイトコードはオブジェクト参照を操作します。オブジェクトは型を指します。タイプは動作を定義します。
この階層を理解すると、CPython の残りの部分が読みやすくなります。起動、インポート、関数呼び出し、例外、ジェネレーター、ガベージ コレクション、シャットダウンはすべて同じランタイム モデルに適合します。