#23. コードオブジェクト

コード オブジェクトは、実行可能な Python コードを CPython がコンパイルして表現したものです。

バイトコードとメタデータが含まれています。インタプリタはそれを実行できますが、コード オブジェクト自体は、グローバル、デフォルト引数、クロージャ セル、バインドされたメソッドなどの実行時状態を保持しません。

このソースの場合:python def add(a, b): return a + b 関数オブジェクトaddコードオブジェクトが含まれています:```python code = add.code

print(code.co_name) print(code.co_varnames) print(code.co_consts) print(code.co_names)


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

コード オブジェクトはコンパイルの出力です。```text
source text
    
tokenization
    
parsing
    
AST
    
symbol table
    
compiler
    
code object
    
frame execution
```コード オブジェクトは、コンパイラとインタプリタ間の受け渡しです。

コンパイラはコード オブジェクトを生成します。

評価ループはフレーム内のコード オブジェクトを実行します。

## 23.2 コードオブジェクトと関数オブジェクト

関数オブジェクトはコード オブジェクトをラップします。

例:```python
def f(x):
    return x + 1
```実行時:```text
function object
    __code__       code object
    __globals__    module globals dictionary
    __defaults__   positional defaults
    __kwdefaults__ keyword-only defaults
    __closure__    closure cells
    __dict__       function attributes
    __name__       function name
    __qualname__   qualified name
```コード オブジェクトにはコンパイルされた本体が含まれます。```text
code object
    bytecode
    constants
    names
    local variable names
    free variable names
    cell variable names
    filename
    line information
    stack size
    flags
```理論的には、同じコード オブジェクトを複数の関数オブジェクトで使用できます。```python
import types

def f(x):
    return x + 1

g = types.FunctionType(f.__code__, globals(), "g")

print(g(10))
```コードは共有されています。関数ラッパーは実行時のバインディングを変更します。

## 23.3 コードオブジェクトは不変です

コードオブジェクトは不変です。

コード オブジェクトのバイトコード、定数、変数名、フラグ、およびメタデータは、作成後に変更することはできません。

これは安全性とランタイム設計にとって重要です。複数の関数またはフレームが同じコード オブジェクトを参照する場合があります。コード オブジェクトが変更可能な場合、コード オブジェクトを変更すると、現在実行中のコードに影響を与える可能性があります。

コードを変更するには、ツールは新しいコード オブジェクトを作成します。

最新の Python は、`replace()`方法:```python
def f():
    return 1

new_code = f.__code__.replace(co_name="renamed")
```これにより、元のオブジェクトを編集するのではなく、変更されたコピーが作成されます。

## 23.4 モジュールコードオブジェクト

モジュールにはコード オブジェクトもあります。

例:```python
src = """
x = 1
y = 2
print(x + y)
"""

code = compile(src, "example.py", "exec")
```このコード オブジェクトはトップレベルの実行を表します。

関数パラメータはありません。通常、そのローカル名前空間はモジュール辞書です。

検査:```python
print(code.co_name)
print(code.co_filename)
print(code.co_consts)
print(code.co_names)
print(code.co_varnames)
```モジュールコードの場合、`co_name`多くの場合`"<module>"`

インタプリタは、グローバルとローカルを使用してこのコード オブジェクトを実行します。通常のインポート、スクリプトの実行、および`exec()`すべてモジュール スタイルのコード オブジェクトを使用します。

## 23.5 式コードオブジェクト`compile()`式コード オブジェクトを生成することもできます。

例:```python
code = compile("1 + 2", "<input>", "eval")
result = eval(code)

print(result)
```アン`eval`code オブジェクトは 1 つの式を表します。式の値を返します。

これは、`exec`モード:```python
compile("x = 1", "<input>", "exec")
compile("1 + 2", "<input>", "eval")

execモードはステートメントを受け入れます。evalモードは式のみを受け入れます。

このモードは、文法の開始点と生成されたコード オブジェクトの動作の両方を変更します。

23.6 インタラクティブなコードオブジェクト

インタラクティブモードで使用するもの"single"編集。

例:```python code = compile("1 + 2", "", "single") exec(code)


これが、REPL がスクリプトの実行とは異なる動作をする理由です。```text
exec mode:
    execute statements normally

eval mode:
    return expression value

single mode:
    behave like interactive input
```コード オブジェクトには、インタプリタが選択したモードを正しく実行するために十分な情報が記録されます。

## 23.7 入れ子になったコードオブジェクト

ネストされた実行可能構造は、ネストされたコード オブジェクトを生成します。

例:```python
def outer(x):
    def inner(y):
        return x + y
    return inner
```モジュール コード オブジェクトには、次のコード オブジェクトが含まれています。`outer``co_consts`

`outer`code オブジェクトには、次のコード オブジェクトが含まれています`inner`それ自体で`co_consts`

検査:```python
def outer(x):
    def inner(y):
        return x + y
    return inner

outer_code = outer.__code__
print(outer_code.co_consts)

for const in outer_code.co_consts:
    if isinstance(const, type(outer_code)):
        print("nested code:", const.co_name)
```ネストされたコード オブジェクトは、それを囲んでいるコード オブジェクトに埋め込まれたコンパイルされたデータであるため、定数です。

実行時、`MAKE_FUNCTION`これらのコード オブジェクトから関数オブジェクトを作成します。

##23.8`co_code`

`co_code`バイトコードバイトを格納します。

例:```python
def f(a, b):
    return a + b

print(f.__code__.co_code)
````co_code`直接読むのは楽しくありません。使用`dis`:

```python
import dis

dis.dis(f)
```バイトコードは、CPython の仮想マシンの命令ストリームです。

各命令にはオペコードがあり、場合によっては引数もあります。正確なエンコーディングはバージョンごとに異なるため、ツールは次を使用する必要があります。`dis`バイトオフセットをハードコーディングするのではなく。

##23.9`co_consts`

`co_consts`バイトコードによって参照される定数を格納します。

例:```python
def f():
    return 1, "x", None
```検査:```python
print(f.__code__.co_consts)
```典型的な内容:```text
(None, 1, 'x')
```定数には次のものが含まれる場合があります。```text
None
booleans
numbers
strings
bytes
tuples of constants
frozensets
nested code objects
```コンパイラは、コード オブジェクト内の一部の定数の重複を排除します。

バイトコード命令はインデックスによって定数をロードします。```text
LOAD_CONST 1
```「負荷」を意味します`co_consts[1]`

##23.10`co_names`

`co_names`グローバル検索、属性検索、インポート、および同様の操作に使用される名前を保管します。

例:```python
def f(xs):
    return len(xs)
```検査:```python
print(f.__code__.co_names)
```考えられる出力:```text
('len',)
```バイトコードはインデックスを使用します。`co_names`:

```text
LOAD_GLOBAL 0
```意味は「グローバル名または組み込み名をロードする」`co_names[0]`

属性アクセスの例:```python
def f(obj):
    return obj.value

valueに登場するco_names属性名もそこに保存されるためです。

##23.11co_varnames

co_varnamesローカル変数名を格納します。

例:python def f(a, b): c = a + b return c 検査:python print(f.__code__.co_varnames) 一般的な出力:text ('a', 'b', 'c') 高速ローカル バイトコードは、このタプルへのインデックスを使用します。```text LOAD_FAST 0 a LOAD_FAST 1 b STORE_FAST 2 c


##23.12`co_freevars`そして`co_cellvars`クロージャは 2 つのコード オブジェクト フィールドを使用します。

|フィールド |意味 |
| ------------- | -------------------------------------- |
|`co_cellvars`|ネストされたスコープによってキャプチャされたローカル |
|`co_freevars`|囲んでいるスコープからキャプチャされた変数 |

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

    def inner():
        return x

    return inner
```検査:```python
print(outer.__code__.co_cellvars)

inner = outer()
print(inner.__code__.co_freevars)
```予想される形状:```text
('x',)
('x',)
```のために`outer``x`ネストされたコードではセル変数が必要であるため、これはセル変数です。

のために`inner``x`は、囲んでいるスコープから取得されるため、自由変数です。

## 23.13 クロージャセルはコードオブジェクトに格納されない

コード オブジェクトは、必要な空き変数を記録します。実際に取得された値は保存されません。

キャプチャされた値は、関数オブジェクトにアタッチされたクロージャ セル内に存在します。

例:```python
def make_reader(value):
    def read():
        return value
    return read

f = make_reader(42)

print(f.__code__.co_freevars)
print(f.__closure__)
print(f.__closure__[0].cell_contents)
```コードオブジェクトには次のように書かれています。```text
this function needs a free variable named value
```関数オブジェクトは以下を提供します。```text
the actual cell containing 42
```この分離により、同じネストされたコード オブジェクトを異なるキャプチャ値で再利用できるようになります。

##23.14`co_argcount`および引数のメタデータ

コード オブジェクトには引数の数が格納されます。

重要なフィールドには次のものが含まれます。```text
co_argcount
co_posonlyargcount
co_kwonlyargcount
co_varnames
co_flags
```例:```python
def f(a, b, /, c, *, d):
    return a, b, c, d
```検査:```python
code = f.__code__

print(code.co_argcount)
print(code.co_posonlyargcount)
print(code.co_kwonlyargcount)
print(code.co_varnames)
```これらのフィールドは、関数呼び出し機構が引数をローカル変数スロットにバインドするのに役立ちます。

デフォルトはコード オブジェクトに保存されません。これらは関数オブジェクト上に存在します。```python
def f(x=1):
    return x

print(f.__defaults__)
print(f.__code__.co_consts)
```デフォルトは関数定義時に評価されるため、デフォルト値は関数オブジェクトに属します。

##23.15`co_flags`

`co_flags`実行フラグを格納します。

フラグは次のようなプロパティを記述します。```text
has *args
has **kwargs
is generator
is coroutine
is async generator
uses nested scopes
future flags
```例:```python
def normal():
    return 1

def gen():
    yield 1

async def coro():
    return 1
```検査:```python
print(normal.__code__.co_flags)
print(gen.__code__.co_flags)
print(coro.__code__.co_flags)
```ランタイムはこれらのフラグを使用して、関数が呼び出されたときにどのオブジェクトを作成するかを決定します。

ジェネレーター関数はジェネレーター オブジェクトを返します。

コルーチン関数はコルーチン オブジェクトを返します。

通常の関数は通常どおり実行され、値を返します。

##23.16`co_stacksize`

`co_stacksize`コード オブジェクトに必要な評価スタックの最大の深さを記録します。

例:```python
def f(a, b, c):
    return a + b * c
```コンパイラーは、バイトコードのスタック効果からスタック使用量を計算します。

概念的には:```text
LOAD_FAST a       stack depth 1
LOAD_FAST b       stack depth 2
LOAD_FAST c       stack depth 3
BINARY_OP *       stack depth 2
BINARY_OP +       stack depth 1
RETURN_VALUE      stack depth 0
```最大深さ: 3

フレームが使用しているのは、`co_stacksize`十分なスタック ストレージを割り当てます。

## 23.17 ソースメタデータ

コード オブジェクトにはソース メタデータが格納されます。

重要なフィールドには次のものが含まれます。```text
co_filename
co_name
co_qualname
co_firstlineno
line table data
position table data
```例:```python
def f():
    x = 1
    return x

code = f.__code__

print(code.co_filename)
print(code.co_name)
print(code.co_qualname)
print(code.co_firstlineno)
```このメタデータは以下をサポートします。```text
tracebacks
debuggers
profilers
coverage tools
inspection
warnings
error locations
```ソース メタデータがなくても Python の実行は可能ですが、診断はさらに悪化します。

## 23.18 ラインテーブルと位置

コード オブジェクトは、バイトコード オフセットをソース位置にマッピングします。

位置を検査できます。```python
def f(x):
    return x + 1

for item in f.__code__.co_positions():
    print(item)
```このソース マッピングは、正確なトレースバックとデバッグをサポートします。

古い CPython バージョンでは、異なる行番号テーブル形式が使用されていました。最新のバージョンでは、列オフセットを含む、より豊富な位置情報が公開されています。

ツールはプライベート バイナリ テーブルを直接解析するのではなく、パブリック メソッドを優先する必要があります。

## 23.19 例外テーブル

最新の CPython コード オブジェクトには、例外テーブル情報が含まれています。

例外テーブルは、どのバイトコード範囲が例外ハンドラーによって保護されるかを説明します。

例:```python
def f():
    try:
        risky()
    except ValueError:
        recover()
```バイトコードだけでは十分ではありません。インタプリタには次のようなメタデータも必要です。```text
protected instruction range
handler target
stack depth restoration information
handler kind
```このメタデータにより、CPython を実装できるようになります。`try``except``finally`、および関連する制御フロー。

## 23.20 コードオブジェクトの作成`compile()`の`compile()`組み込みはコードオブジェクトを作成します。

例:```python
code = compile("x = 1\n", "<input>", "exec")

ns = {}
exec(code, ns)

print(ns["x"])
```式の場合:```python
code = compile("1 + 2", "<input>", "eval")

print(eval(code))
```AST 入力の場合:```python
import ast

tree = ast.parse("x = 1\n")
code = compile(tree, "<ast>", "exec")

compile()ファイルに使用されるのと同じ広範なパイプラインを実行します。```text source or AST ↓ validation ↓ symbol analysis ↓ bytecode generation ↓ code object


コードオブジェクトは次のように実行できます。`exec()`または`eval()`。

例:```python
code = compile("x = 10\n", "<input>", "exec")

globals_dict = {}
locals_dict = {}

exec(code, globals_dict, locals_dict)

print(locals_dict["x"])

exec()ランタイム名前空間を提供します。

コード オブジェクト自体には、これらの名前空間は含まれません。

式コードの場合:```python code = compile("x + 1", "", "eval")

print(eval(code, {"x": 41}))


## 23.22 コードオブジェクトとフレーム

フレームは、コード オブジェクトの実行中のインスタンスです。

コードオブジェクト:```text
immutable compiled instructions
```フレーム:```text
current execution state
instruction pointer
local variables
evaluation stack
block state
globals
builtins
exception state
```例:```python
def f(x):
    y = x + 1
    return y
```それぞれの電話に`f`実行中のフレームを作成または使用します`f.__code__`

複数の呼び出しでは同じコード オブジェクトが使用されますが、異なるフレーム状態が使用されます。```text
f.__code__
    shared by all calls

frame for f(1)
    x = 1
    y = 2

frame for f(10)
    x = 10
    y = 11
```## 23.23 コードオブジェクトと`marshal`CPython ではコード オブジェクトをシリアル化できます。`marshal`。

コンパイル済み`.pyc`ファイルには、マーシャリングされたコード オブジェクトとヘッダー メタデータが含まれています。

これは実装固有のものです。の`marshal`format は安定した汎用シリアル化形式ではありません。

実際的なルール:```text
use pickle or another format for application data
use marshal only for CPython internals or closely related tooling
```Python がモジュールをインポートするとき、CPython はキャッシュされたモジュールをロードする場合があります。`.pyc`ファイルを読み取って、ソースを再コンパイルする代わりにコード オブジェクトを読み取って実行します。

## 23.24 コードオブジェクト`.pyc`ファイル

`.pyc`ファイルには、コンパイルされたバイトコード キャッシュ データが保存されます。

概念的には:```text
.pyc file
    header
        magic number
        invalidation metadata
    marshalled module code object
```モジュール コード オブジェクトには、ネストされた関数およびクラス本体のコード オブジェクトが含まれる場合があります。`co_consts`

バイトコード キャッシュは、ソースが変更されていない場合に解析とコンパイルの繰り返しを回避することで、起動を高速化します。

しかし`.pyc`ファイルはバージョン固有です。バイトコードとマーシャル形式は、CPython のバージョン間で変更される可能性があります。

## 23.25 コードオブジェクトのセキュリティ境界

コード オブジェクトは実行可能データです。

コード オブジェクトの実行は、コードの実行と同じです。

例:```python
code = compile("import os; os.remove('file.txt')", "<input>", "exec")
exec(code)
```コード オブジェクトはすでにコンパイルされているため、安全になりません。

セキュリティに敏感なシステムでは、信頼できないコード オブジェクト、信頼できないソース、または信頼できないマーシャリングされたバイトコードを実行しないでください。

コンパイルはサンドボックス化ではありません。

## 23.26 コードオブジェクトのイントロスペクション

コード オブジェクトは内省に役立ちます。

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

code = f.__code__

for name in [
    "co_argcount",
    "co_posonlyargcount",
    "co_kwonlyargcount",
    "co_nlocals",
    "co_stacksize",
    "co_flags",
    "co_consts",
    "co_names",
    "co_varnames",
    "co_freevars",
    "co_cellvars",
]:
    print(name, getattr(code, name))
```これは次のようなツールをサポートします。```text
debuggers
profilers
coverage tools
tracers
decorators
test frameworks
bytecode inspectors
static analysis helpers
```## 23.27 コードオブジェクトの置換

コード オブジェクトは、次を使用して変更を加えてコピーできます。`replace()`

例:```python
def f():
    return 1

new_code = f.__code__.replace(co_name="g")
```これは高度なツールに役立ちます。

しかし、コードオブジェクトの内部を変更すると、簡単に仮定に反する可能性があります。たとえば、バイトコード、定数、名前、スタック サイズ、フラグ、例外テーブルは一貫性を保つ必要があります。

コード オブジェクトが壊れていると、ツールがクラッシュしたり、紛らわしいエラーが発生したり、誤った動作をしたりする可能性があります。

使用`replace()`メタデータの編集や慎重に検証されたバイトコード作業の場合。

## 23.28 コードオブジェクトはバージョン固有です

コード オブジェクト フィールドとバイトコードの詳細は、Python のバージョンによって異なります。

バージョンに依存する領域の例:```text
opcode names
opcode arguments
inline cache layout
exception table format
line table format
code object constructor signature
optimization behavior
```堅牢なツールはパブリック API を使用する必要があります。```text
dis
inspect
types.CodeType.replace
co_positions()
co_lines()
ast
compile
```生のバイトコードのレイアウトが安定していると仮定することは避けてください。

## 23.29 CPython ソース領域

重要な CPython ソース ファイルには次のものが含まれます。```text
Include/cpython/code.h
Objects/codeobject.c
Python/compile.c
Python/assemble.c
Python/flowgraph.c
Python/marshal.c
Lib/dis.py
Lib/inspect.py
Lib/types.py
```概念的な役割:

|エリア |役割 |
| -------------- | ---------------------------------- |
|`code.h`|コードオブジェクト構造の定義 |
|`codeobject.c`|コードオブジェクトの作成と動作 |
|`compile.c`| ASTから命令生成へ |
|`assemble.c`|最終的なコードオブジェクトアセンブリ |
|`flowgraph.c`|制御フローグラフ処理 |
|`marshal.c`|バイトコードキャッシュのシリアル化 |
|`dis.py`|人間が判読できるバイトコード検査 |

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

このモデルを使用します。```text
A code object is immutable compiled Python code.
It contains bytecode plus metadata.
A function object wraps a code object with runtime context.
A frame executes a code object.
Nested functions, lambdas, classes, comprehensions, and generators have nested code objects.
Constants, names, locals, free variables, and cell variables are stored in code object tables.
The interpreter uses the code object to allocate frames and run bytecode.
```コード オブジェクトは、CPython のコンパイラを仮想マシンに接続するコンパイルされた成果物です。