#6. ソースコードから実行まで

CPython は Python ソース テキストを直接実行しません。最初のバイトコード命令が実行される前に、いくつかの内部表現を通じてソース テキストを変換します。

パスは次のとおりです。```text source text ↓ tokens ↓ parse tree ↓ abstract syntax tree ↓ symbol table ↓ code object ↓ frame ↓ bytecode evaluation ↓ object operations


## 6.1 ソーステキスト

入力はテキストとして始まります。```python
x = 1 + 2
print(x)
```CPython はこれを実行する前に、次のことを認識している必要があります。```text
where statements begin and end
which characters form names
which characters form numbers
which indentation levels define blocks
which tokens form expressions
which names are local or global
which bytecode instructions are needed
```Python のソース コードは単なる文字列ではありません。エンコーディング、行構造、インデント、コメント、文字列リテラル規則、および構文規則があります。

最初の段階では、生のテキストをトークンに変換します。

## 6.2 トークン化

トークナイザーはソース文字を読み取り、トークンを生成します。

このコードの場合:```python
x = 1 + 2
```トークナイザーは次のようなストリームを生成します。```text
NAME("x")
EQUAL("=")
NUMBER("1")
PLUS("+")
NUMBER("2")
NEWLINE
ENDMARKER
```ブロック構造の場合、インデントもトークンになります。```python
if ok:
    run()
else:
    stop()
```概念的には:```text
NAME("if")
NAME("ok")
COLON
NEWLINE
INDENT
NAME("run")
LPAR
RPAR
NEWLINE
DEDENT
NAME("else")
COLON
NEWLINE
INDENT
NAME("stop")
LPAR
RPAR
NEWLINE
DEDENT
ENDMARKER
```これは重要です。 Python のブロック構造は、後で空白から推測されることはありません。トークナイザーは明示的なメッセージを出力します`INDENT`そして`DEDENT`トークン。

## 6.3 解析

パーサーはトークンを消費し、それらが有効な Python 構文を形成しているかどうかをチェックします。

のために:```python
x = 1 + 2
```パーサーは、右辺がバイナリ式である代入ステートメントを認識します。

のために:```python
def add(a, b):
    return a + b
```パーサーは以下を認識します。```text
function definition
function name
parameter list
function body
return statement
binary expression
```パーサーは無効なトークン シーケンスを拒否します。```python
x = + * 3
```これはパーサーに到達しますが、有効な構文に分解することはできません。

パーサーの出力は、プログラムの構造化表現です。次に、CPython はその構造を AST に変換します。

## 6.4 抽象構文ツリー

AST はプログラムの意味構造を表します。

のために:```python
x = 1 + 2
```AST は概念的には次のとおりです。```text
Module
    Assign
        target: Name("x", Store)
        value:
            BinOp
                left: Constant(1)
                op: Add
                right: Constant(2)
```AST は多くの表面の詳細を削除し、後のコンパイラ段階で必要な構造を保持します。

Python から AST を検査できます。```python
import ast

tree = ast.parse("x = 1 + 2")
print(ast.dump(tree, indent=4))
```出力形状の例:```text
Module(
    body=[
        Assign(
            targets=[
                Name(id='x', ctx=Store())],
            value=BinOp(
                left=Constant(value=1),
                op=Add(),
                right=Constant(value=2)))],
    type_ignores=[])
```AST は、プログラムが構造的に何を意味するかを示します。どのバイトコード命令を発行するかについてはまだ述べられていません。

## 6.5 名前コンテキスト

AST 名にはコンテキストが含まれます。

このコードでは:```python
x = x + 1
```2つの用途`x`異なる役割を持っています。```text
left side x     Store
right side x    Load
```概念的には:```text
Assign
    target: Name("x", Store)
    value:
        BinOp
            left: Name("x", Load)
            op: Add
            right: Constant(1)
```名前のロードと名前の保存は異なる操作にコンパイルされるため、この区別は重要です。```text
Load context     read a value
Store context    assign a value
Del context      delete a binding
```コンパイラはバイトコードを出力するときにこの情報に依存します。

## 6.6 シンボルテーブルの分析

バイトコードを生成する前に、CPython は名前を分析します。

それぞれの名前が次のとおりであるかどうかを決定します。```text
local
global
nonlocal
free
cell
implicit builtin lookup
```例:```python
x = 10

def f(y):
    return x + y
```内部`f`:

```text
y is local
x is global or builtin lookup
```別の例:```python
def outer():
    x = 10

    def inner():
        return x

    return inner
```ここ:```text
x is local in outer
x is free in inner
x becomes a cell variable in outer
```セル変数は、内部関数によってキャプチャされるため、存続する必要があるローカル変数です。自由変数は、関数によって使用されるが、外側のスコープに格納される変数です。

この段階はクロージャにとって不可欠です。

## 6.7 コードオブジェクト

解析とシンボル分析の後、CPython はコードをコード オブジェクトにコンパイルします。

コード オブジェクトには次のものが含まれます。```text
bytecode
constants
names
local variable names
free variable names
cell variable names
stack size
flags
filename
function name
line mapping information
exception table
```コード オブジェクトを検査できます。```python
def add(a, b):
    return a + b

code = add.__code__

print(code.co_name)
print(code.co_varnames)
print(code.co_consts)
print(code.co_names)
print(code.co_freevars)
print(code.co_cellvars)
```コード オブジェクトは不変です。実行可能コードについて説明しますが、ローカル変数の現在の実行時値は含まれません。

## 6.8 バイトコード

バイトコードは CPython の命令形式です。

のために:```python
def add(a, b):
    return a + b
```逆アセンブリは概念的には次のようになります。```text
LOAD_FAST a
LOAD_FAST b
BINARY_OP +
RETURN_VALUE
```実際のバイトコード名とレイアウトは Python のバージョンによって異なります。中心的な考え方は変わりません。つまり、バイトコード命令はフレーム上で動作します。

使用`dis`:

```python
import dis

def add(a, b):
    return a + b

dis.dis(add)
```バイトコードは AST よりも下位レベルです。死刑執行が近づいている。

ASTは次のように述べています。```text
return a + b
```バイトコードには次のように書かれています。```text
load a
load b
perform addition
return result
```## 6.9 定数と名前

コード オブジェクトは、定数と名前をバイトコードとは別に保存します。

のために:```python
x = 10
print(x)
```定数`10`定数テーブルに保存されます。名前`x`そして`print`名前テーブルに保存されます。

概念的には:```text
co_consts = (10, None)
co_names  = ("x", "print")
```次に、バイトコードはインデックスによってこれらのテーブルを参照します。```text
LOAD_CONST 0       load constant 10
STORE_NAME 0       store into name x
LOAD_NAME 1        load name print
LOAD_NAME 0        load name x
CALL 1
POP_TOP
LOAD_CONST 1       load None
RETURN_VALUE
```これによりバイトコードがコンパクトになります。命令は、完全な文字列やオブジェクトではなく、小さなインデックスを保存します。

## 6.10 モジュールの実行

Python ファイルはモジュール レベルのコード オブジェクトにコンパイルされます。

のために:```python
# demo.py
x = 1

def f():
    return x
```CPython は、ファイル全体を 1 つのコード オブジェクトにコンパイルします。そのコード オブジェクトを実行すると、モジュール バインディングが作成されます。

概念的には:```text
create module object
create module dictionary
execute module code object in that dictionary
bind x
create function object f
bind f
```関数本体も独自のコード オブジェクトにコンパイルされます。モジュール コード オブジェクトには、その関数コード オブジェクトが定数として含まれています。

これは、関数を定義するとモジュールのインポート時にコードが実行される理由を説明します。本体は実行されませんが、関数オブジェクトが作成されてバインドされます。

## 6.11 関数の定義

関数定義は実行可能コードです。

のために:```python
def add(a, b):
    return a + b
```CPython does not run the body immediately. It creates a function object.

概念的には:```text
load code object for add
load function name
create function object
store function object in current namespace
```関数オブジェクトには次のものが含まれます。```text
code object
globals dictionary
default values
closure cells
annotations
metadata
```その後、関数が呼び出されると、CPython はその関数オブジェクトからフレームを作成し、関数のコード オブジェクトを実行します。

## 6.12 フレームの作成

CPython がコード オブジェクトを実行すると、フレームが作成されます。

関数呼び出しの場合:```python
add(2, 3)
```CPython は以下を使用してフレームを作成します。```text
code object for add
globals from add.__globals__
builtins
local slots
argument values
value stack
instruction pointer
exception state
```引数はローカル変数スロットに配置されます。```text
a = 2
b = 3
```次に、バイトコード評価器がフレームの実行を開始します。

## 6.13 評価スタック

ほとんどのバイトコード命令は、フレームの値スタックを通じて通信します。

のために:```python
return a + b
```実行は次のとおりです。```text
LOAD_FAST a      push value of a
LOAD_FAST b      push value of b
BINARY_OP +      pop two values, add, push result
RETURN_VALUE     pop result and return it
```ローカル変数は、一時的なスタック値とは別に保存されます。```text
locals:
    a = 2
    b = 3

stack:
    temporary values used by bytecode
```これが、CPython がスタックベースの仮想マシンと呼ばれる理由です。

## 6.14 オブジェクトの操作

バイトコード命令は、生の C プリミティブではなく、Python オブジェクトに対して動作します。

CPython が実行されると、次のようになります。```python
a + b
```それは想定していません`a`そして`b`はマシン整数です。

それらは次のとおりです。```text
integers
floats
strings
lists
tuples
NumPy arrays
user-defined objects
```操作はオブジェクト プロトコルを通じてディスパッチされます。

のために`int + int`, CPythonでは整数加算を使用します。のために`str + str`、文字列連結を使用します。ユーザー定義クラスの場合は、次のように呼び出すことができます。`__add__`

概念的には:```text
BINARY_OP +
    inspect operand types
    find numeric operation
    call appropriate slot
    return Python object
```これが、型が具体的な動作を提供する一方で、バイトコードが汎用的なままである理由です。

## 6.15 属性アクセス

対象:```python
obj.name
```CPython は属性ロードをコンパイルします。

概念的には:```text
LOAD_FAST obj
LOAD_ATTR name
```実行時、`LOAD_ATTR`Python 属性検索ルールを実行します。```text
check type descriptors
check instance dictionary
check non-data descriptors and class attributes
possibly call __getattr__
raise AttributeError if missing
```一般に、属性アクセスは生のフィールド検索ではありません。それはプロトコル操作です。

これは、属性アクセスで Python コードを実行できる理由を説明します。```python
class C:
    @property
    def name(self):
        print("computed")
        return 42

obj = C()
obj.name
```読み取られた属性は記述子コードを呼び出します。

## 6.16 呼び出し

対象:```python
result = f(2, 3)
```CPython は呼び出し可能オブジェクトと引数を評価し、呼び出しを実行します。

概念的には:```text
load f
load 2
load 3
call with 2 positional arguments
store result
```実行時には、呼び出し可能オブジェクトは次のようになります。```text
Python function
built-in C function
bound method
class object
object with __call__
partial object
method descriptor
```Python 関数呼び出しにより、新しいフレームが作成されます。 C 組み込み呼び出しは、C 関数ラッパーを呼び出します。クラス呼び出しは、インスタンスを割り当てて初期化します。

バイトコード命令は汎用です。ランタイムディスパッチによって正確な呼び出しパスが決定されます。

## 6.17 制御フロー

制御フローはジャンプにコンパイルされます。

のために:```python
if x:
    a()
else:
    b()
```CPython は次のような形式のバイトコードを出力します。```text
load x
jump if false to else
call a
jump to end
else:
call b
end:
```For ループは、イテレータ プロトコル操作とジャンプにコンパイルされます。```python
for x in items:
    use(x)
```概念的には:```text
get iterator
loop_start:
    get next item
    if exhausted, jump to loop_end
    store x
    call use(x)
    jump to loop_start
loop_end:
```言語機能は高レベルです。実行モデルはバイトコード ジャンプとプロトコル呼び出しです。

## 6.18 例外処理

例外処理は、保護されたバイトコード範囲とハンドラー メタデータにコンパイルされます。

のために:```python
try:
    risky()
except ValueError:
    recover()
```CPython は次のことを知る必要があります。```text
which bytecode range is protected
where the handler starts
which exception type to match
how to unwind the stack
where execution continues
```例外が発生すると、エバリュエーターは例外処理メタデータを参照し、一致する場合は適切なハンドラーに制御を渡します。

現在のフレーム内に一致するハンドラーがない場合、例外は呼び出し元に伝播します。

## 6.19 インポート

import ステートメントは実行可能コードです。```python
import math
```実行時に、CPython はインポート機構を使用して次のことを行います。```text
check sys.modules
find a module spec
load or create the module
execute module code if needed
bind the name
```インポート システムは部分的に Python で実装されています。`importlib`C ランタイム コードによって部分的にサポートされています。

モジュール ファイルは他の Python コードと同じようにコンパイルおよび実行されますが、その実行名前空間はモジュール ディクショナリです。

## 6.20 内包表記

内包表記には独自の実行スコープがあります。

のために:```python
squares = [x * x for x in range(10)]
```CPython は内包表記本体のコードを作成します。

概念的には:```text
call range(10)
get iterator
create result list
run comprehension code
append each computed value
store final list in squares
```ループ変数`x`周囲の関数スコープではなく、内包表記の内部スコープに属します。

その理由は次のとおりです。```python
[x for x in range(3)]
print(x)
```束縛しない`x`最新の Python の周囲のスコープ内。

## 6.21 クロージャ

クロージャにはセルが必要です。

のために:```python
def outer():
    x = 10

    def inner():
        return x

    return inner

innerからの変数を使用しますouter

CPythonは保存できませんx通常の高速ローカルとして、次の場合に消滅します。outer戻ります。保管しますxセルオブジェクト内。

概念的には:text outer local x becomes cell variable inner references x as free variable inner function stores reference to the cell cell keeps x alive after outer returns これが、返された関数が引き続き機能する理由です。```python f = outer() print(f())


## 6.22 ジェネレーター

ジェネレーター関数は、通常の関数とは異なる方法でコンパイルされます。```python
def count():
    yield 1
    yield 2
```電話をかける`count()`ジェネレーターオブジェクトを作成します。すぐに体が動くわけではありません。

ジェネレーター オブジェクトは、中断された実行状態を保存します。```text
code object
frame or frame-like execution state
instruction offset
local variables
value stack state
running status
```それぞれ`next()`次まで実行を再開します`yield````python
g = count()
next(g)
next(g)
````yield`単なる返品ではありません。フレームを一時停止し、実行状態を保存します。

## 6.23 コルーチン

コルーチンはジェネレーターに似ていますが、`await`プロトコル。```python
async def fetch():
    value = await operation()
    return value
```電話をかける`fetch()`コルーチンオブジェクトを作成します。本体は、コルーチンが待機またはスケジュールされている場合にのみ実行されます。

コルーチンは中断された実行状態を保存し、前後に再開します。`await`ポイント。

概念的には:```text
create coroutine object
start execution
reach await
suspend coroutine
resume later with result
continue execution
return final value
```イベント ループはコア バイトコード モデルの外にありますが、コルーチンの一時停止と再開は CPython オブジェクトとフレームによって実装されるランタイム機能です。

## 6.24 クラス定義

クラスステートメントは実行可能なコードです。```python
class C:
    x = 1

    def f(self):
        return self.x
```CPython は単に静的型を割り当てるだけではありません。一時的な名前空間でクラス本体を実行します。

概念的には:```text
load class name
prepare class namespace
execute class body code object
collect attributes and methods
call metaclass
bind resulting class object to name C
```これは、クラス本体に任意のコードを含めることができる理由を説明しています。```python
class C:
    print("building class")
    x = 1 + 2
```クラス本体は、class ステートメントが実行されるとすぐに実行されます。

## 6.25 エンドツーエンドの例

次のことを考慮してください。```python
x = 10

def add(y):
    return x + y

print(add(5))
```パイプラインは次のとおりです。```text
tokenize source
parse tokens
build AST
analyze symbols
compile module code object
execute module frame
    bind x = 10
    create function object add
    bind add
    load print
    load add
    load 5
    call add
        create function frame
        bind y = 5
        load global x
        load local y
        add objects
        return 15
    call print
finish module execution
```重要な点は、最初の行が実行される前に CPython がすでにかなりの作業を行っているということです。

## 6.26 各ステージが存在する場所

便利なソース マップ:```text
Tokenizer          Parser/
Parser             Parser/ and Grammar/
AST support        Python/ast.c
Symbol table       Python/symtable.c
Compiler           Python/compile.c
Code objects       Objects/codeobject.c
Function objects   Objects/funcobject.c
Frames             Python/ and Objects/frameobject.c
Evaluation loop    Python/ceval.c and generated/interpreter files
Objects            Objects/
Imports            Lib/importlib/ and Python/import.c
```正確なファイル名は時間の経過とともに変化しますが、このマップはリポジトリを読み取るのに十分安定しています。

## 6.27 メンタルモデル

このコンパクトなモデルを維持します。```text
Source code becomes tokens.
Tokens become syntax.
Syntax becomes AST.
AST plus scope analysis becomes bytecode.
Bytecode lives in code objects.
Code objects execute inside frames.
Frames use local slots and a value stack.
Bytecode instructions operate on PyObject references.
Types decide concrete behavior.
```システム全体は大規模ですが、このシーケンスがバックボーンです。

## 6.28 章の概要

CPython の実行はパイプラインです。ソース テキストから始まり、バイトコード エバリュエーター内のオブジェクト操作で終わります。トークナイザーは文字を処理します。パーサーは構文を処理します。 ASTは構造を表します。記号テーブルは名前を分類します。コンパイラはコード オブジェクトを生成します。フレームはコード オブジェクトを実行します。バイトコードは、型定義の動作を通じて Python オブジェクトを操作します。

このパイプラインは、高レベルの Python 構造がどのように具体的なランタイム アクションになるかを説明します。