#8。PyObjectそしてPyVarObject

PyObjectそしてPyVarObjectは、CPython オブジェクトの背後にある基本レイアウトです。これらは Python クラスではありません。これらは、ランタイムが共通のポインター型を通じて多くの異なるオブジェクト実装を処理できるようにする C レベルの構造体規則です。

実行時、CPython のほとんどのオブジェクト参照は次のように表されます。```c PyObject *


## 8.1 共通オブジェクトヘッダー

簡略化された`PyObject`次のようになります:```c
typedef struct {
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;
```実際の定義では、特にデバッグ ビルド、トレース ビルド、および最新の CPython バージョンで、マクロとビルド依存フィールドが使用されます。しかし、本質的なアイデアは安定しています。```text
PyObject
    reference count
    type pointer
```参照カウントは所有権を追跡します。

型ポインタは、オブジェクトがどのように動作するかを CPython に伝えます。

通常の CPython オブジェクトはすべて、この共通ヘッダーで始まります。そのため、ランタイムは`PyObject *`コンパイル時に完全な具体的な構造体を知らなくても、その型を検査できます。

## 8.2 すべてのオブジェクトが同じように始まる理由

次の Python コードを考えてみましょう。```python
x = 42
y = "hello"
z = [1, 2, 3]
```C レベルでは、これらのオブジェクトのレイアウトは異なります。```text
PyLongObject
    object header
    integer digit data

PyUnicodeObject
    object header
    string metadata
    character storage

PyListObject
    object header
    length
    allocated capacity
    pointer to item array
```ただし、それぞれは同じヘッダーで始まります。```text
+--------------------+
| ob_refcnt          |
+--------------------+
| ob_type            |
+--------------------+
| type-specific data |
+--------------------+
```これにより、汎用のランタイム コードがすべてのオブジェクトで動作できるようになります。

例えば、`Py_INCREF(obj)`参照カウントフィールドのみが必要です。かどうかを知る必要はありません`obj`リスト、文字列、辞書、または関数です。

同じく、`Py_TYPE(obj)`タイプポインタフィールドのみが必要です。

##8.3`ob_refcnt`

`ob_refcnt`オブジェクトの参照カウントを保存します。

参照カウントは、CPython の主要なオブジェクト存続期間メカニズムです。コードがオブジェクト参照を作成、保存、返し、または解放すると、CPython はこの数を更新します。

簡略化:```c
#define Py_INCREF(op) ((op)->ob_refcnt++)
#define Py_DECREF(op)                         \
    do {                                      \
        if (--(op)->ob_refcnt == 0) {         \
            deallocate_object(op);            \
        }                                     \
    } while (0)
```実際の実装はさらに複雑です。不滅オブジェクト、デバッグフック、トレース、フリースレッドビルド、および割り当て解除の詳細を処理します。

概念的なルールは次のとおりです。```text
new strong reference acquired
    increment reference count

strong reference released
    decrement reference count

reference count reaches zero
    destroy object
```例:```python
x = []
y = x
del x
del y
```リスト オブジェクトは、少なくとも 1 つの強参照が残っている限り存続します。

##8.4`ob_type`

`ob_type`オブジェクトの型オブジェクトを指します。

この Python コードの場合:```python
x = []
```リストオブジェクトの`ob_type`を指します`list`型オブジェクト。

概念的には:```text
x  --->  PyListObject
            ob_refcnt
            ob_type  ---->  PyList_Type
            ob_size
            ob_item
            allocated
```type オブジェクトは動作を記述します。```text
object name
object size
base classes
method table
attribute lookup behavior
call behavior
deallocation behavior
number operations
sequence operations
mapping operations
```これが、CPython がオペレーションをディスパッチする方法です。

コードが評価されるとき:```python
len(x)
```CPython は型の長さスロットをチェックします。

コードが評価されるとき:```python
x[0]
```CPython はシーケンスまたはマッピングの動作をチェックします。

コードが評価されるとき:```python
x + y
```CPython は、型に応じて数値またはシーケンスの連結スロットをチェックします。

##8.5`PyObject_HEAD`拡張タイプは通常、フィールドを手動で書き込みません。彼らはマクロを使用します。

固定サイズの拡張オブジェクトは、多くの場合次のように始まります。```c
typedef struct {
    PyObject_HEAD
    long value;
} CounterObject;

PyObject_HEADオブジェクトヘッダーに必要なフィールドに展開されます。

概念的には:```c typedef struct { Py_ssize_t ob_refcnt; PyTypeObject *ob_type; long value; } CounterObject;


## 8.6 固定サイズのオブジェクト

固定サイズのオブジェクトは、その型のすべてのインスタンスで同じ C 構造体サイズを持ちます。

形状の例:```c
typedef struct {
    PyObject_HEAD
    double value;
} FloatLikeObject;
```すべてのインスタンスには 1 つだけのスペースがあります`double`

多くのオブジェクトはオブジェクト構造体レベルで固定サイズです。```text
float
module
function
method
cell
weakref
many iterator objects
many descriptor objects
```オブジェクトは引き続き、外部データまたは個別に割り当てられたデータを参照する可能性があります。固定サイズとは、オブジェクト構造体自体が固定サイズであることを意味し、完全な論理オブジェクトに補助記憶域がないことを意味するものではありません。

関数オブジェクトは構造体として固定サイズですが、コード オブジェクト、グローバル辞書、デフォルト タプル、クロージャ タプル、注釈などの他のオブジェクトを指します。

## 8.7`PyVarObject`可変サイズのオブジェクトの使用`PyVarObject`。

簡略化されたレイアウト:```c
typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size;
} PyVarObject;
```伸びる`PyObject`追加のフィールドを使用して:```text
ob_size

ob_size通常、オブジェクトの論理サイズが格納されます。

例:text tuple length bytes length integer digit count some internal variable-sized arrays 一般的な形状:text PyVarObject ob_refcnt ob_type ob_size variable object payload ## 8.8PyObject_VAR_HEAD可変サイズの拡張タイプは以下を使用します。c typedef struct { PyObject_VAR_HEAD PyObject *items[1]; } SmallArrayObject; 概念的には:```c typedef struct { Py_ssize_t ob_refcnt; PyTypeObject *ob_type; Py_ssize_t ob_size; PyObject *items[1]; } SmallArrayObject;


このパターンは、オブジェクトのサイズが割り当て後に固定される場合に使用されます。

タプルは標準的な例です。タプルの長さは作成後に変更されないため、CPython はオブジェクト ヘッダーと項目参照を含む 1 つのブロックを割り当てることができます。

## 8.9 何を`ob_size`手段`ob_size`「このオブジェクトが占有するバイト数」を意味するものではありません。

これは、タイプ固有のサイズ値を意味します。

タプルの場合は要素の数です。

バイトの場合はバイト数です。

長整数の場合、内部の基本桁の数に関連し、符号をエンコードする場合があります。

カスタム型の場合、その意味は型の実装によって異なります。

これは重要です。`ob_size`オブジェクトのタイプ固有のコードによって解釈されます。```text
same field
different meaning per type
```型オブジェクトは、それ自体のインスタンスを解釈する方法を知っています。

## 8.10 タプルのレイアウト

タプルは可変サイズ オブジェクトの良い例です。

概念的には:```c
typedef struct {
    PyObject_VAR_HEAD
    PyObject *ob_item[1];
} PyTupleObject;
```長さ 3 のタプルは、3 つの項目参照用のスペースを持つ 1 つのオブジェクトとして割り当てられます。```text
PyTupleObject
    ob_refcnt
    ob_type  ---> tuple type
    ob_size = 3
    ob_item[0] ---> object A
    ob_item[1] ---> object B
    ob_item[2] ---> object C
```タプルはその項目への参照を所有します。タプルが破棄されると、含まれている各オブジェクトの参照カウントがデクリメントされます。

タプルはオブジェクトを排他的に所有しません。参照を所有しています。```python
a = []
t = (a,)
```タプルにはリストへの参照が保存されます。名前`a`同じリストへの参照も保存します。

## 8.11 リストのレイアウト

リストも Python レベルでは可変長ですが、その実装はタプルとは異なります。

リスト オブジェクトには、個別に割り当てられた項目参照の配列を指す固定サイズの構造体があります。

概念的には:```c
typedef struct {
    PyObject_VAR_HEAD
    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;
```形:```text
PyListObject
    ob_refcnt
    ob_type  ---> list type
    ob_size = current length
    ob_item  ----> separately allocated array
    allocated = current capacity
```リストの場合:```python
xs = [10, 20, 30]
```メモリの形状はおよそ次のとおりです。```text
list object
    ob_size = 3
    allocated >= 3
    ob_item ----+
                |
                v
              [ ptr to 10 ][ ptr to 20 ][ ptr to 30 ][ spare capacity... ]
```これにより、効率的な追加が可能になります。リスト オブジェクト自体を移動せずに、個別の項目配列を再割り当てすることで、リストを拡大できます。

オブジェクトのアイデンティティは安定したままです。```python
xs = []
before = id(xs)

xs.append(1)
xs.append(2)
xs.append(3)

after = id(xs)

print(before == after)   # True
```リストの内部配列は移動する可能性があります。リスト オブジェクト自体は同じオブジェクトのままです。

## 8.12 オブジェクトが動かない理由

CPython は通常、ライブ オブジェクトを移動しません。

`PyObject *`はダイレクトポインタです。 CPython の多くの部分と多くの C 拡張機能は、そのポインターを保持している可能性があります。

CPython がメモリ内のオブジェクトを移動した場合、そのオブジェクトへのすべてのポインターを検索して更新する必要があります。これは高価であり、多くの C 拡張コードと互換性がありません。

したがって、CPython は不動オブジェクト モデルを使用します。

結果:```text
object identity can be represented by address in CPython
C extensions can hold PyObject * pointers
objects are not compacted by a moving garbage collector
memory fragmentation must be managed differently
```これが、CPython のアロケーター設計が重要である理由の 1 つです。

## 8.13 オブジェクト型間のキャスト

すべてのオブジェクトは共通のヘッダーで始まるため、CPython は具体的なオブジェクト ポインターを次のオブジェクトにキャストできます。`PyObject *`

例:```c
PyObject *obj = (PyObject *)some_list;
```ただし、逆キャストは型チェック後にのみ安全です。```c
if (PyList_Check(obj)) {
    PyListObject *list = (PyListObject *)obj;
}
```安全でないキャストはメモリを破損したり、インタープリタをクラッシュさせたりする可能性があります。

正しい拡張コードは次のパターンに従います。```c
static PyObject *
get_size(PyObject *self, PyObject *arg)
{
    if (!PyList_Check(arg)) {
        PyErr_SetString(PyExc_TypeError, "expected list");
        return NULL;
    }

    Py_ssize_t n = PyList_GET_SIZE(arg);
    return PyLong_FromSsize_t(n);
}
````PyList_GET_SIZE`マクロは引数がリストであることを前提としています。型が不確かな場合は、チェックされた API バリアントの方が安全です。

## 8.14 チェックされた API と高速マクロ

CPython は、チェックされた関数と高速マクロの両方を公開します。

チェックされたフォーム:```c
Py_ssize_t n = PyList_Size(obj);
```高速マクロ形式:```c
Py_ssize_t n = PyList_GET_SIZE(obj);
```チェックされた関数はオブジェクトを検証し、入力が無効な場合はエラーを報告します。

マクロは、オブジェクトがすでに有効であり、フィールドに直接アクセスできることを前提としています。

トレードオフ:

|フォーム |安全性 |  スピード |使用例 |
| ---------------- | -----: | -----: | -------------------------------- |
|チェックした機能 |より高い |   |公共の境界線、不確実な入力 |
|高速マクロ |   |より高い |検証後の内部コード |

このパターンは C API 全体に現れます。

## 8.15 タイプオブジェクトサイズフィールド

type オブジェクトはインスタンスのサイズを表します。

重要なフィールドには次のものが含まれます。```text
tp_basicsize
tp_itemsize

tp_basicsizeは各インスタンスの固定部分です。tp_itemsize可変サイズ オブジェクトの各可変サイズ項目のサイズです。

固定サイズ型の場合:text tp_basicsize = sizeof(MyObject) tp_itemsize = 0 可変サイズ型の場合:text tp_basicsize = base header and fixed fields tp_itemsize = size of each trailing item その後、割り当ては次のことを計算できます。```text total size = tp_basicsize + n * tp_itemsize


## 8.16 最小の固定サイズ拡張オブジェクト

最小限の固定サイズのオブジェクト レイアウト:```c
typedef struct {
    PyObject_HEAD
    long value;
} CounterObject;
```最小限のタイプのオブジェクト スケッチ:```c
static PyTypeObject CounterType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "example.Counter",
    .tp_basicsize = sizeof(CounterObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT,
};
```重要な点は構造です。```text
CounterObject starts with PyObject header.
CounterType says how large CounterObject is.
CPython allocates memory according to CounterType.
CPython treats the result as PyObject * at generic boundaries.
```## 8.17 最小の可変サイズ拡張オブジェクト

可変サイズのオブジェクトのレイアウトは次のようになります。```c
typedef struct {
    PyObject_VAR_HEAD
    PyObject *items[1];
} FixedArrayObject;
```型オブジェクトはゼロ以外の項目サイズを使用します。```c
static PyTypeObject FixedArrayType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "example.FixedArray",
    .tp_basicsize = offsetof(FixedArrayObject, items),
    .tp_itemsize = sizeof(PyObject *),
    .tp_flags = Py_TPFLAGS_DEFAULT,
};
```割り当てでは、特定の論理長が要求されます。

概念的には:```text
allocate FixedArray with n items
    total bytes = tp_basicsize + n * tp_itemsize
    ob_size = n
```このレイアウトは、含まれる参照の数が作成時にわかっていて、その後変更されない場合に便利です。

## 8.18 オブジェクトヘッダーマクロ

一般的なマクロには次のものがあります。```c
Py_REFCNT(obj)
Py_TYPE(obj)
Py_SIZE(obj)
```それらの概念的な意味は次のとおりです。```text
Py_REFCNT(obj)
    get reference count

Py_TYPE(obj)
    get type pointer

Py_SIZE(obj)
    get variable-size field
```例えば:```c
PyTypeObject *type = Py_TYPE(obj);
```そして:```c
Py_ssize_t n = Py_SIZE(tuple_obj);
```拡張コードは、フィールドへの直接アクセスよりも公式のマクロと関数を優先する必要があります。これにより、コードと CPython の変更との互換性が高まります。

## 8.19 参照の所有権とヘッダー

オブジェクトヘッダーにはカウントが保存されます。それ自体では所有権を説明できません。

所有権は、API ルールによって強制される規則です。

新しい参照を返す関数は、呼び出し元に所有権を譲渡します。```c
PyObject *x = PyLong_FromLong(42);
/* caller owns x */
Py_DECREF(x);
```借用した参照を返す関数は所有権を譲渡しません。```c
PyObject *item = PyList_GetItem(list, 0);
/* borrowed reference, do not DECREF unless INCREF first */
```どちらの場合も同じオブジェクト ヘッダーが関係します。違いは、API 呼び出しのコントラクトです。

これが、CPython 拡張プログラミングが難しい理由です。メモリのレイアウトはシンプルですが、所有権のルールには規律が必要です。

## 8.20 オブジェクトの初期化

割り当てと初期化は別のものです。

型オブジェクトの場合、割り当ては通常次のように処理されます。```text
tp_alloc
```オブジェクトの構築には次のことが含まれる場合があります。```text
tp_new
tp_init
```Python レベルでは:```python
obj = MyClass(...)
```おおよそ次のことを意味します:```text
call type object
    call tp_new to allocate or return object
    call tp_init to initialize object
    return object
```不変オブジェクトの場合、`tp_new`オブジェクトが公開される前に値を確立する必要があるため、ほとんどの作業を実行することがよくあります。

可変オブジェクトの場合、`tp_init`割り当て後に状態を埋めることができます。

## 8.21 割り当て解除

オブジェクトの参照カウントがゼロになると、CPython は型固有のデアロケーターを呼び出します。

type オブジェクトはこれを次の場所に保存します。```text
tp_dealloc
```通常、割り当て解除機能は次の手順を実行します。```text
release references owned by the object
free auxiliary buffers
untrack from cyclic GC if needed
free object memory
```コンテナーの場合、デアロケーターは含まれているオブジェクトへの参照をデクリメントする必要があります。

形状の例:```c
static void
Counter_dealloc(CounterObject *self)
{
    Py_TYPE(self)->tp_free((PyObject *)self);
}
```コンテナの場合:```c
static void
Array_dealloc(ArrayObject *self)
{
    for (Py_ssize_t i = 0; i < Py_SIZE(self); i++) {
        Py_XDECREF(self->items[i]);
    }

    Py_TYPE(self)->tp_free((PyObject *)self);
}
```これは簡略化されています。実際のコードは、ガベージ コレクターの追跡とエラー セーフな不変式を処理する必要があります。

## 8.22 ガベージコレクターヘッダー

周期的なガベージ コレクションに参加するオブジェクトには、表示されるヘッダーの前に追加の GC ヘッダーがある場合があります。`PyObject`ヘッダ。

概念的には:```text
GC header
PyObject header
type-specific payload
````PyObject *`GC ヘッダーではなく、オブジェクト ヘッダーを指します。```text
memory block start
    GC metadata
    ob_refcnt       <--- PyObject * points here
    ob_type
    payload
```GC ヘッダーは、オブジェクトをコレクター構造にリンクします。

通常、この追跡が必要なのは、サイクルに参加できるコンテナのようなオブジェクトだけです。

他の Python オブジェクトへの参照を所有し、サイクルに参加できる拡張タイプは、GC プロトコルを正しく実装する必要があります。

## 8.23 デバッグビルド

デバッグ ビルドでは、追加のフィールドが追加されたり、オブジェクトの周囲がチェックされたりする場合があります。

これには次のものが含まれます。```text
reference count debugging
allocator padding
forbidden bytes around memory blocks
extra assertions
API misuse detection
```このため、拡張コードは、文書化されたマクロを超えて正確な生のメモリ レイアウトを想定することを避ける必要があります。

悪いスタイル:```c
obj->ob_refcnt++;
```より良いスタイル:```c
Py_INCREF(obj);
```悪いスタイル:```c
obj->ob_type
```より良いスタイル:```c
Py_TYPE(obj)
```マクロは、拡張コードと CPython 内部間の互換性レイヤーです。

## 8.24 不滅のオブジェクト

最新の CPython には、選択された長命オブジェクトに対する不滅オブジェクトの概念があります。不滅オブジェクトは特別な参照カウント値を使用し、通常の参照カウントの破壊を回避します。

典型的な候補には、基本的なシングルトンのようなオブジェクトやランタイム所有のオブジェクトが含まれます。

社内関係者向けの実用的なポイント:```text
not every reference count behaves like an ordinary small integer
not every INCREF or DECREF has the same runtime effect
never write code that depends on exact refcount arithmetic unless working inside CPython internals
```Python レベルでは、参照カウント検査はすでに実装固有になっています。 C レベルでは、拡張機能の作成者は公式のリファレンス管理 API を使用する必要があります。

## 8.25 なぜ`PyObject`問題`PyObject`CPython ランタイムの共通通貨です。

インタプリタループのプッシュとポップ`PyObject *`価値観。

関数呼び出しが通過する`PyObject *`引数。

コンテナストア`PyObject *`参考文献。

C 拡張 API が受信して返す`PyObject *`

タイプスロットが動作する`PyObject *`

エラーは Python 例外オブジェクトによって表されます。

モジュールは Python オブジェクトです。

クラスは Python オブジェクトです。

関数は Python オブジェクトです。

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

この均一性が Python を動的にするものです。ランタイムは、型オブジェクトを通じて動作をディスパッチしながら、1 つの表現を通じて任意の値を操作できます。

## 8.26 有用なメンタルモデル

CPython C コードを読むときは、まず次の形を想定します。```text
PyObject *
    points to object header
        ob_refcnt
        ob_type
    followed by type-specific memory
```次に次のように尋ねます。```text
What concrete type is this object expected to be?
Is it fixed-size or variable-size?
Who owns this reference?
Can this object participate in reference cycles?
What type slot handles this operation?
Does this API return a new reference or borrowed reference?
```これらの質問により、CPython の内部を読む際の初期の混乱をほとんど防ぐことができます。

## 8.27 概要`PyObject`CPython オブジェクトの基本ヘッダーです。各オブジェクトに参照カウントと型ポインタを与えます。`PyVarObject`可変サイズのオブジェクトで使用されるサイズ フィールドを使用してヘッダーを拡張します。

固定サイズのオブジェクトの使用`PyObject_HEAD`

可変サイズのオブジェクトの使用`PyObject_VAR_HEAD`

オブジェクト ヘッダーにより、CPython の動的オブジェクト システムが可能になります。これにより、汎用ランタイム コードで多くの具体的なオブジェクト レイアウトを処理できるようになります。`PyObject *`一方、型オブジェクトは、呼び出し、算術演算、インデックス付け、属性、割り当て解除、およびプロトコルのディスパッチに必要な動作を提供します。