24. 定数、名前、ローカル変数
#24. 定数、名前、ローカル変数
コード オブジェクトは、Python ソース コードをテキストとして保存しません。コンパクトなテーブルと、それらのテーブルをインデックスで参照するバイトコード命令が格納されます。
最も重要なテーブルは次の 3 つです。```text id="mx2a9w" co_consts co_names co_varnames
この関数の場合:```python id="wg80pj"
def f(a):
b = len(a)
return b + 1
```CPython ストア:```text id="jxh1ou"
constants:
None
1
names:
len
local variables:
a
b
```バイトコード命令はインデックスによってそれらのエントリを参照します。
## 24.1 コンパイルパイプラインでの位置
定数、名前、およびローカルはコンパイル中にアセンブルされます。```text id="la84x8"
AST
↓
symbol table
↓
compiler
↓
instruction stream
↓
constant table
↓
name table
↓
local variable table
↓
code object
```これらのテーブルはコード オブジェクトの一部です。
インタプリタはバイトコードを実行するときにそれらを使用します。```text id="gvqxes"
LOAD_CONST 1
LOAD_GLOBAL 0
LOAD_FAST 0
STORE_FAST 1
```各数値オペランドは、コード オブジェクトのテーブルの 1 つにインデックス付けされます。
## 24.2 コードオブジェクトがテーブルを使用する理由
バイトコードはコンパクトである必要があります。
CPython では、完全な Python オブジェクトまたは文字列を各命令内に直接埋め込むのではなく、それらをテーブルに一度格納します。
例:```python id="y6ymck"
def f():
print("hello")
print("hello")
```文字列`"hello"`に一度現れることができます`co_consts`。
名前`"print"`に一度現れることができます`co_names`。
バイトコードはインデックスによってそれらを参照します。
概念的には:```text id="y1yh5e"
co_consts:
0: None
1: "hello"
co_names:
0: "print"
bytecode:
LOAD_GLOBAL 0 print
LOAD_CONST 1 "hello"
CALL
POP_TOP
LOAD_GLOBAL 0 print
LOAD_CONST 1 "hello"
CALL
POP_TOP
```これにより重複が減り、インタプリタに安定した検索位置が与えられます。
## 24.3 定数
定数はリテラル値とコンパイルされたアーティファクトであり、`co_consts`。
一般的な定数には次のものがあります。```text id="0sqmi6"
None
True
False
integers
floats
complex numbers
strings
bytes
tuples of constants
frozensets of constants
nested code objects
```例:```python id="0bigls"
def f():
return 123, "abc", None
```検査:```python id="unotfl"
print(f.__code__.co_consts)
```典型的な形状:```text id="zsqktw"
(None, 123, 'abc')
```関数によって返されるタプルは、それ自体が定数から構築される場合もありますが、コンパイラーが安全に折りたたむことができる場合は、定数タプルとして格納される場合もあります。
## 24.4`None`定数
ほとんどのコード オブジェクトには次のものがあります。`None`で`co_consts`。
例:```python id="mxsc3g"
def f():
x = 1
```ソースに明確な記載がないにもかかわらず、`return None`、関数は戻ります`None`暗黙的に。
コンパイラは以下を発行できる必要があります。```text id="b7i1tf"
LOAD_CONST None
RETURN_VALUE
```モジュールレベルでは、CPython は多くの場合、最終的な`RETURN_VALUE`と`None`実行を終了します。
## 24.5 数値定数
数値リテラルは通常、`co_consts`。
例:```python id="y88hr2"
def f():
return 10 + 20
```AST には以下が含まれる場合があります`10 + 20`ですが、コンパイラはこれを次のように折り畳む可能性があります。`30`。
検査:```python id="y7a6vi"
import dis
def f():
return 10 + 20
print(f.__code__.co_consts)
dis.dis(f)
```あなただけが見るかもしれません`30`ロードされた定数として。
一定の折り畳みは保守的です。 CPython は、コンパイル時に安全かつ決定的な式を折りたたむことができます。ランタイム効果を含む式を折りたたむことはできません。
例:```python id="3nempa"
def f():
return 10 + x
xはコンパイル時には不明であるため、これを定数にすることはできません。
24.6 文字列定数とバイト定数
文字列リテラルとバイトリテラルは次のように表示されます。co_consts。
例:python id="086py8" def f(): return "hello", b"world" 検査:python id="mwit9a" print(f.__code__.co_consts) 隣接する文字列リテラルはコンパイル中に連結される場合があります。```python id="yn2nfn"
def f():
return "hello" "world"
結果は 1 つの定数になります。```text id="g8bxm7"
"helloworld"
```## 24.7 タプル定数
定数のみを含むタプルを定数として保存できます。
例:```python id="u33byh"
def f():
return (1, 2, 3)
```コンパイラはタプル全体を保存する場合があります。```text id="kw4dlg"
(1, 2, 3)
```で`co_consts`。
ただし、要素が動的である場合は次のようになります。```python id="f0psq9"
def f(x):
return (1, x, 3)
```タプルは実行時に構築する必要があります。
コンパイルパターン:```text id="0f7svq"
LOAD_CONST 1
LOAD_FAST x
LOAD_CONST 3
BUILD_TUPLE 3
```コンパイラは、不変の定数コンテナとランタイムで構築されたコンテナを区別します。
## 24.8 固定セット定数
セット リテラルは変更可能であるため、セット自体をバイトコードの定数にすることはできません。
ただし、CPython は使用する可能性があります`frozenset`最適化されたメンバーシップ テストの定数。
例:```python id="a4bjga"
def is_small(x):
return x in {1, 2, 3}
```ソースではセットリテラルが使用されています。コンパイラは、メンバーシップをコンパイルする場合があります。`frozenset`このセットはメンバーシップのテストのみに使用されるため、定数です。
概念的には:```text id="y0dtbp"
LOAD_FAST x
LOAD_CONST frozenset({1, 2, 3})
CONTAINS_OP
```これにより、関数が実行されるたびにセットを再構築することがなくなります。
コンパイラはセマンティクスを保持する必要があります。この最適化は定数に対して有効です。`{1, 2, 3}`そして`frozenset({1, 2, 3})`関連する操作に対して同じ結果が得られます。
## 24.9 定数としてのネストされたコードオブジェクト
ネストされた関数はコード オブジェクトを次の場所に保存します。`co_consts`。
例:```python id="n17lr5"
def outer():
def inner():
return 1
return inner
```検査:```python id="56jqwc"
for const in outer.__code__.co_consts:
print(repr(const))
```次のコードオブジェクトが見つかります。`inner`。
実行時に、外側の関数は、ネストされたコード オブジェクトをロードして関数オブジェクトを作成するバイトコードを実行します。```text id="xrarxf"
LOAD_CONST <code object inner>
MAKE_FUNCTION
STORE_FAST inner
```ネストされたコード オブジェクトは、コンパイルされた定数データです。関数オブジェクトは実行時に作成されます。
## 24.10 名前テーブル`co_names`高速ローカル変数またはクロージャ変数ではないバイトコード操作で使用されるシンボリック名を格納します。
これらには次のものが含まれます。```text id="b9fx1t"
global names
builtin lookup names
attribute names
imported module names
method names
some class body names
```例:```python id="sl10xc"
def f(xs):
return len(xs)
```検査:```python id="il1b6j"
print(f.__code__.co_names)
```典型的な形状:```text id="6whwgp"
('len',)
```バイトコードがロードされます`len`名前インデックスを介して。
## 24.11 グローバルとビルトイン
の名前`co_names`実行時にグローバルまたは組み込みを参照する場合があります。
例:```python id="pxxc09"
def f(xs):
return len(xs)
lenローカルではありません。グローバル検索ルールを使用してロードされます。
実行時の検索チェック:text id="d2z1ce" function globals then builtins コード オブジェクトには実際の組み込み関数は格納されません。len。名前文字列を格納します"len"。
つまり、グローバルを変更すると実行に影響する可能性があります。```python id="oghwkx" def f(xs): return len(xs)
len = lambda x: 999
print(f([1, 2, 3]))
```そのモジュール内では、len組み込みではなくグローバル ラムダに解決されるようになりました。
24.12 属性名
属性名も存在しますco_names。
例:python id="trjd66" def f(obj): return obj.value 検査:python id="vrg5um" print(f.__code__.co_names) 典型的な形状:text id="133tbx" ('value',) バイトコードは以下を使用します。text id="f1yae8" LOAD_FAST obj LOAD_ATTR value 文字列"value"に一度保存されますco_names。
これはグローバル ルックアップとは別のものです。同じテーブルには、複数の命令ファミリーで使用される名前文字列が格納されます。
24.13 メソッド名
メソッド呼び出し構文でも名前が使用されます。
例:```python id="pwnbfj" def f(obj): return obj.run()
`run`に登場する`co_names`。
コンパイラは、Python のバージョンに応じて特殊な呼び出し指向の命令を発行する場合がありますが、メソッド名は依然として名前テーブルから取得されます。
概念的なバイトコード:```text id="vjlnib"
LOAD_FAST obj
LOAD_METHOD run
CALL 0
```名前テーブルにはメソッド名の文字列が格納されます。
## 24.14 名前のインポート
インポートでも名前テーブルを使用します。
例:```python id="sf2lb3"
def f():
import os
return os.getcwd()
```名前テーブルには以下が含まれる場合があります。```text id="pi43tc"
os
getcwd
```ローカルテーブルには以下が含まれます`os`インポートはローカル変数をバインドするため、インポートが関数内にある場合。
この区別は重要です。```text id="hc8fxf"
co_names:
names needed by import and attribute operations
co_varnames:
local binding created by import os
```インポートは、ローカル変数レイアウトの観点から見た代入です。
## 24.15 ローカル変数`co_varnames`高速ローカル変数名を保存します。
例:```python id="nyh7qv"
def f(a, b):
c = a + b
return c
```検査:```python id="g0h75x"
print(f.__code__.co_varnames)
```典型的な形状:```text id="ja0vd0"
('a', 'b', 'c')
```フレームは、配列のようなレイアウトでローカル値を格納します。バイトコードはこのレイアウトにインデックスを付けます。```text id="x503qw"
LOAD_FAST 0 a
LOAD_FAST 1 b
STORE_FAST 2 c
```これが、ローカル アクセスがグローバル辞書検索よりも高速である理由です。
## 24.16 パラメータローカル
関数パラメータは最初に表示されます`co_varnames`。
例:```python id="9b48mv"
def f(a, b, c=0):
d = a + b + c
return d
co_varnames通常は次のように始まります。```text id="83pbbd"
('a', 'b', 'c', 'd')
デフォルトは、コード オブジェクトに直接保存されるのではなく、関数オブジェクトに保存されます。```python id="9q7w3m"
print(f.__defaults__)
print(f.__code__.co_varnames)
```## 24.17 位置のみおよびキーワードのみのローカル
コード オブジェクトは引数の数を別個に保存します。`co_varnames`。
例:```python id="87asnp"
def f(a, b, /, c, *, d):
return a, b, c, d
```検査:```python id="kbjcbq"
code = f.__code__
print(code.co_posonlyargcount)
print(code.co_argcount)
print(code.co_kwonlyargcount)
print(code.co_varnames)
```ローカル名は次の場所に保存されます。`co_varnames`一方、カウントは先頭のエントリを解釈する方法を定義します。
概念的には:```text id="h79uky"
co_varnames:
a, b, c, d
co_posonlyargcount:
2
co_argcount:
3
co_kwonlyargcount:
1
```呼び出し機構は、引数のバインドにこのメタデータを使用します。
##24.18`*args`そして`**kwargs`変数引数名は、`co_varnames`。
例:```python id="a78joh"
def f(a, *args, **kwargs):
return args, kwargs
```検査:```python id="57zit6"
code = f.__code__
print(code.co_varnames)
print(code.co_flags)
```の`co_flags`フィールドには、関数が変数の位置引数またはキーワード引数を受け入れることが記録されます。
名前`args`そして`kwargs`ローカルスロットです。
## 24.19 一時値にはローカルという名前が付けられません
評価スタックには一時的な値が保持されます。
例:```python id="yqtwg0"
def f(a, b, c):
return a + b * c
co_varnames含まれるもの:text id="gvos5r" a b c などの一時的な中間値は含まれません。b * c。
その中間結果はフレームの評価スタック上に存在します。
概念的なスタック:```text id="3snx1b" LOAD_FAST a stack: a LOAD_FAST b stack: a, b LOAD_FAST c stack: a, b, c BINARY_OP * stack: a, temp BINARY_OP + stack: result RETURN_VALUE stack empty
## 24.20 セル変数`co_cellvars`ネストされた関数によってキャプチャされたローカルを格納します。
例:```python id="qbiuv6"
def outer():
x = 1
def inner():
return x
return inner
```検査:```python id="91ti0b"
print(outer.__code__.co_cellvars)
```典型的な結果:```text id="n2byuv"
('x',)
```内部`outer`、`x`細胞の中で生きていなければならないので、`inner`後にアクセスできる可能性があります`outer`戻ります。
ローカル変数はヒープバッククロージャストレージになります。
## 24.21 自由変数`co_freevars`囲んでいるスコープから取得した名前を格納します。
同じ例を使用すると、次のようになります。```python id="b3corm"
inner = outer()
print(inner.__code__.co_freevars)
```典型的な結果:```text id="i1hd7z"
('x',)
```内部`inner`、`x`普通の地元民ではありません。クロージャセルからロードされます。
概念的なバイトコード:```text id="o8g8eg"
LOAD_DEREF x
RETURN_VALUE
```コード オブジェクトには名前が記録されます。関数オブジェクトは実際のセルを保持します。
## 24.22 地元の辞書
通常の最適化された関数内では、ローカル