#21. シンボルテーブル

解析によって AST が生成された後、CPython はスコープ分析を実行します。

この段階ではシンボル テーブルを構築します。

シンボル テーブルには、各スコープ内で名前がどのように動作するかが記録されます。これにより、名前がローカル、グローバル、フリー、セル、パラメータ、インポート、注釈付き、またはネストされたスコープから参照されるかどうかが決まります。

このソースの場合:```python x = 10

def outer(): y = 20

def inner():
    return x + y

return inner

シンボル テーブル フェーズによって次のことが決まります。```text
x       global from inner
y       free variable in inner
y       cell variable in outer
inner   local variable in outer
outer   global variable in module
```バイトコード生成はそれに依存するため、この分析は不可欠です。ローカル変数のロードには、グローバル変数またはクロージャ変数のロードとは異なるバイトコードが使用されます。

## 21.1 コンパイルパイプラインでの位置

シンボル テーブル フェーズは、AST 構築とバイトコード生成の間にあります。```text
source
tokenization
parsing
AST
symbol table analysis
compiler
code object
bytecode execution
```パーサーは構文構造を構築します。

シンボル テーブル フェーズでは、スコープ構造を構築します。

コンパイラは両方を使用します。

## 21.2 シンボルテーブルの内容

各スコープにはシンボル テーブルがあります。

シンボル テーブルには次のような情報が保存されます。```text
which names exist
where names are assigned
where names are read
whether names are parameters
whether names are imported
whether names are global
whether names are nonlocal
whether names escape into nested scopes
whether names require closure cells
```概念的には:```text
Scope: function outer

Symbols:
    y       local + cell
    inner   local
Scope: function inner

Symbols:
    x       global
    y       free
```シンボル テーブル フェーズではコードは実行されません。静的なスコープ分類を実行します。

## 21.3 Python は字句スコープを使用する

Python は字句スコープを使用します。

ネストされた関数は、外側のスコープから名前にアクセスできます。

例:```python
def outer():
    x = 10

    def inner():
        return x

    return inner

innerアクセスできるxなぜならx周囲の語彙環境に存在します。

シンボル テーブル フェーズは、ランタイム実行前​​にこの関係を決定します。

字句スコープ分析がなければ、CPython は次のことを認識できません。```text where to look for a name whether to allocate closure storage whether a name belongs in locals whether a variable escapes into nested functions


Python にはいくつかのスコープ タイプがあります。

|範囲 | |
| ------------------- | ----------------- |
|モジュールの範囲 |最上位ファイル |
|機能範囲 |`def f():`|
|クラススコープ |`class C:`|
|理解範囲 |`[x for x in xs]`|
|ジェネレーターのスコープ |`(x for x in xs)`|
|ラムダスコープ |`lambda x: x + 1`|

各スコープには独立したシンボル分析があります。

例:```python
x = 1

def f():
    x = 2
    return x
```モジュールと関数にはそれぞれ独自のものがあります`x`

シンボル テーブルのフェーズによってそれらが分離されます。

## 21.5 モジュールのスコープ

最上位のコードはモジュール スコープで実行されます。

例:```python
x = 1
y = 2

def add():
    return x + y
```モジュールスコープでは:```text
x       global/module-local
y       global/module-local
add     global/module-local
```内部`add`:

```text
x       global
y       global
```実行時に、グローバルはモジュール ディクショナリに保存されます。

シンボル テーブル フェーズにより、内部の参照が決定されます。`add`ローカル ルックアップ バイトコードではなくグローバル ルックアップ バイトコードを使用する必要があります。

## 21.6 関数のスコープ

関数本体はローカル スコープを作成します。

例:```python
def f(a, b):
    c = a + b
    return c
```内部`f`:

```text
a       parameter + local
b       parameter + local
c       local
```パラメータは初期化されたローカル変数として扱われます。

コンパイラは後でフレーム オブジェクト内のローカルにスロットを割り当てます。

シンボル テーブル フェーズによって次のことが決まります。```text
how many local variables exist
which names are parameters
which bytecode instructions to emit
```## 21.7 ローカル変数

関数内の任意の場所に割り当てられた名前は、別途宣言されない限りローカルになります。

例:```python
x = 10

def f():
    print(x)
    x = 20
```これにより、以下が発生します。```text
UnboundLocalError
```なぜ?

シンボルテーブルフェーズではへの割り当てが見られるため、`x`内部`f`:

```python
x = 20
```つまり:```text
x is local to f
```先ほど:```python
print(x)
```ローカルを読み取ろうとします`x`任務の前に。

この動作は、実行時の推測ではなく、静的なスコープ分析から生じます。

## 21.8 グローバル宣言`global`スコープの分類を変更します。

例:```python
x = 10

def f():
    global x
    x = 20
```これで、シンボル テーブルに次のことが記録されます。```text
x       global
```内部`f`

この割り当てにより、モジュール レベルの変数が更新されます。

それなし`global`、代入するとローカル変数が作成されます。

シンボルテーブルフェーズは処理する必要があります`global`名前アクセスをコンパイルする前の宣言。

## 21.9 非ローカル宣言`nonlocal`囲む関数スコープ変数を指します。

例:```python
def outer():
    x = 10

    def inner():
        nonlocal x
        x += 1

    inner()
    return x
```内部`inner`:

```text
x       free variable
```内部`outer`:

```text
x       cell variable
```セル変数は、ネストされたスコープによってキャプチャされるローカル変数です。

それなし`nonlocal`、内部の割り当て`inner`新しいローカル変数が作成されます。`nonlocal`シンボルテーブルに次のように指示します。```text
do not create a local binding
use enclosing function binding
```## 21.10 自由変数

自由変数はスコープ内で使用される名前ですが、それを囲む関数スコープ内で定義されます。

例:```python
def outer():
    x = 10

    def inner():
        return x
```内部`inner`:

```text
x       free variable

xはローカルではありませんinner

それはから来ていますouter

自由変数にはクロージャのサポートが必要です。

21.11 セル変数

セル変数は、ネストされたスコープによって参照されるローカル変数です。

例:```python def outer(): x = 10

def inner():
    return x

```内部outer:

x       local + cell
```なぜ?

なぜなら`x`その後も生き残らなければならない`outer`戻ります。

入れ子になった関数には引き続きアクセスする必要があります。

CPython は、キャプチャした変数を、通常のスタックのみのローカルではなく、ヒープに割り当てられたクロージャ セルに格納します。

シンボル テーブル フェーズにより、どのローカルにセル ストレージが必要かが決まります。

## 21.12 クロージャ

クロージャはシンボル テーブル分析に直接依存します。

例:```python
def make_counter():
    count = 0

    def inc():
        nonlocal count
        count += 1
        return count

    return inc
```シンボル テーブルは次のことを決定します。```text
count in make_counter:
    local + cell

count in inc:
    free
```次に、コンパイラはクロージャ機構を生成します。

実行時:```python
c = make_counter()
```キャプチャされたファイルへのアクセスを運ぶ関数を返します。`count`細胞。

シンボルテーブル分析がなければ、クロージャの構築は不可能です。

## 21.13 名前解決カテゴリ

CPython では、名前を大まかにカテゴリに分類します。

|カテゴリー |意味 |
| --------------- | -------------------------------- |
|ローカル |現在のスコープで定義されています |
|グローバル明示的 |で宣言`global`|
|グローバル暗黙 |モジュール/組み込みレベルで解決 |
|無料 |囲んでいるスコープから取得されます |
|セル |ネストされたスコープによってキャプチャされたローカル |

この分類はバイトコード生成を制御します。

バイトコード カテゴリの例:

|カテゴリー |バイトコード ファミリ |
| ----------- | --------------- |
|ローカル |`LOAD_FAST`|
|グローバル |`LOAD_GLOBAL`|
|無料/セル |`LOAD_DEREF`|
|名前の検索 |`LOAD_NAME`|

スコープの決定が異なると、実行時の動作とパフォーマンスも異なります。

## 21.14 バイトコードはスコープ分析に依存します

例:```python
def f(a):
    return a
```バイトコード:```text
LOAD_FAST a
RETURN_VALUE

aローカルです。

今:```python x = 1

def f(): return x バイトコード:text LOAD_GLOBAL x RETURN_VALUE クロージングケース:python def outer(): x = 1

def inner():
    return x

```内部inner:

LOAD_DEREF x
RETURN_VALUE
```パーサーだけではこれらの命令を決定できません。

シンボル テーブル フェーズは、必要なスコープ情報を提供します。

## 21.15 クラスのスコープ

クラス本体には特別なスコープ動作があります。

例:```python
x = 1

class C:
    y = x
```クラス本体内:```text
y       local to class namespace
x       global
```クラス本体は、独自の名前空間ディクショナリ内で実行されます。

ただし、メソッドはクラス スコープの変数を字句的に自動的にキャプチャしません。

例:```python
class C:
    x = 1

    def f(self):
        return x
```こちらはアクセスしません`C.x`。

名前`x`内部`f`特に定義されていない限り、グローバルとして扱われます。

クラス スコープは重要な点で関数スコープとは異なります。

## 21.16 内包範囲

最新の Python 内包表記は独自のスコープを作成します。

例:```python
x = 100

values = [x for x in range(3)]

print(x)
```出力:```text
100
```内包変数は外側のスコープに漏れません。

内部的に:```text
comprehension creates nested scope
x inside comprehension is local there
outer x remains unchanged
```以前の Python バージョンでは動作が異なりました。最新の CPython は分離された内包スコープを使用します。

## 21.17 ジェネレーター式のスコープ

ジェネレーター式もネストされたスコープを作成します。

例:```python
x = 10

g = (x for x in range(3))

print(x)
```アウター`x`は変わらないままです。

ジェネレーター式は、暗黙的な入れ子関数と同様に動作します。

シンボル テーブル フェーズでは、ジェネレーター スコープ用に個別のシンボル情報を作成します。

## 21.18 ラムダスコープ

ラムダ式は関数スコープを作成します。

例:```python
x = 10

f = lambda y: x + y
```ラムダの内部:```text
y       local parameter
x       free variable
```ラムダは匿名関数ですが、シンボル テーブル分析では、ラムダを通常の入れ子関数と同様に扱います。

## 21.19 インポートとシンボルテーブル

インポートはシンボル テーブルにも影響します。

例:```python
import os
from math import sin
```モジュールスコープのシンボル:```text
os      imported local/global
sin     imported local/global
```内部関数:```python
def f():
    import json

jsonローカルになるf

インポートは、スコープ分析の観点からの割り当てです。

21.20 注釈

変数アノテーションはシンボル処理に参加します。

例:python x: int = 1 シンボル テーブルには次の情報が記録されます。text x assigned 関数の注釈も表示されます。```python def f(x: int) -> str: return str(x)


## 21.21 例外ハンドラー変数

例外ハンドラー名はローカル バインディングを作成します。

例:```python
try:
    run()
except ValueError as e:
    print(e)
```例外ブロック内:```text
e       local
```CPython は、トレースバックを伴う参照サイクルを避けるために、ハンドラーの後で例外変数をクリアします。

シンボル テーブル フェーズはバインディング自体を識別します。クリーンアップ動作は後で行われます。

## 21.22 パターンマッチングバインディング

パターン マッチングではバインディング ルールが導入されます。

例:```python
match value:
    case [x, y]:
        print(x, y)
```パターン変数はローカル バインディングになります。

シンボル テーブル フェーズでは、以下を区別する必要があります。```text
pattern capture
ordinary expression
```パターン構文のセマンティクスが異なるためです。

## 21.23 シンボル分析中の AST トラバーサル

シンボル テーブル フェーズは AST を歩きます。

簡略化したモデル:```text
visit Module
    create module scope

visit FunctionDef
    register function name
    create child scope
    visit parameters
    visit body

visit Assign
    mark targets as assigned
    visit expression

visit Name
    mark as load/store/delete

visit Global
    record explicit global declaration

visit Nonlocal
    record nonlocal declaration
```トラバーサルでは、まず情報が収集され、次にスコープの関係が解決されます。

## 21.24 スコープ分析の 2 パスの性質

スコープ分析は純粋にローカルなものではありません。

例:```python
def f():
    print(x)
    x = 1
```最初の意味`x`後の割り当てに依存します。

したがって、CPython は単一の左から右へのパスで名前を分類できません。

シンボル テーブル フェーズでは、最終的な分類の前に、ブロック全体にわたるスコープ情報を効果的に収集します。

## 21.25 ネストされたスコープツリー

スコープはツリーを形成します。

例:```python
x = 0

def outer():
    y = 1

    def inner():
        z = 2
```スコープツリー:```text
Module
    outer
        inner
```各スコープには、子スコープへのリンクが保存されます。

自由変数の解決は、囲んでいるスコープを介して外側に進みます。

## 21.26 ビルトイン

名前がローカルでもグローバルでもモジュール定義されていない場合、実行時の検索は組み込みにフォールバックする可能性があります。

例:```python
print(len([1, 2, 3]))
```モジュールのスコープ内:```text
print     implicit global/builtin
len       implicit global/builtin
```シンボル テーブル フェーズでは、実際の組み込みオブジェクトは解決されません。ルックアップスタイルを分類します。

実行時に、グローバル ルックアップは以下をチェックします。```text
module globals
then builtins
```## 21.27`symtable`モジュール

Python は、`symtable`モジュール。

例:```python
import symtable

src = """
x = 1

def outer():
    y = 2

    def inner():
        return x + y
"""

table = symtable.symtable(src, "<input>", "exec")

print(table.get_identifiers())
```ネストされたシンボル テーブルはプログラムで検査できます。

このモジュールは次の場合に役立ちます。```text
linters
static analyzers
teaching scope behavior
compiler experiments
```## 21.28 CPython のシンボルテーブルのデータ構造

重要なソース領域には次のものが含まれます。```text
Python/symtable.c
Include/internal/
Python/compile.c
```シンボル テーブル機構には以下が格納されます。```text
scope type
symbol flags
child scopes
free variables
cell variables
parameter information
optimization flags
```シンボル フラグは、次のような動作を記述します。```text
assigned
used
parameter
global
nonlocal
free
cell
imported
annotated
```コンパイラは後でこのメタデータを使用します。

## 21.29 最適化とローカル

スコープ分析により、ローカル変数への高速アクセスが可能になります。

ローカル変数は、フレーム内のインデックス付きスロットを使用します。

例:```python
def f(a, b):
    c = a + b
    return c
```ローカルは配列スタイルのアクセスにコンパイルできます。```text
locals[0] -> a
locals[1] -> b
locals[2] -> c
```これは辞書検索よりもはるかに高速です。

シンボル テーブル分析がなければ、CPython はローカル レイアウトを事前計算できません。

## 21.30 最小限のメンタルモデル

このモデルを使用します。```text
The parser builds syntax structure.
The symbol table phase builds scope structure.
Each module, function, class, lambda, and comprehension creates a scope.
Names are classified as local, global, free, or cell variables.
Nested functions create closure relationships.
The compiler uses symbol table information to generate correct bytecode.
Fast locals, globals, and closures all depend on symbol analysis.
```シンボル テーブル フェーズでは、CPython が構文を実行可能なスコープのセマンティクスに変換します。