12. オブジェクトのレイアウトとタイプ スロット

CPython は、すべてのランタイム値をオブジェクトとして表します。各オブジェクトにはメモリ レイアウトがあり、各オブジェクトのタイプはそのメモリがどのように解釈されるべきかを記述します。

オブジェクトのレイアウトは次のように答えます。text What fields exist inside this object? Where are the references to other Python objects? How large is one instance? Does the object have variable-sized trailing storage? Does the object participate in cyclic garbage collection? タイプ スロットの答えは次のとおりです。```text How is this object called? How is it deallocated? How does attribute lookup work? How does indexing work? How does addition work? How does iteration work? How is it represented as text?


## 12.1 オブジェクトメモリはヘッダーから始まる

通常の CPython オブジェクトはすべてオブジェクト ヘッダーで始まります。

概念的には:```c
typedef struct {
    Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;
```これにより、すべてのオブジェクトに 2 つの基本フィールドが与えられます。

|フィールド |意味 |
| ----------- | ---------------------------- |
|`ob_refcnt`|参照カウント |
|`ob_type`|オブジェクトのタイプへのポインタ |

具体的なオブジェクトは、このヘッダーの後に独自のフィールドを配置します。

形状の例:```text
PyLongObject
    PyObject header
    integer-specific fields

PyListObject
    PyObject / PyVarObject header
    list-specific fields

PyFunctionObject
    PyObject header
    function-specific fields
```すべてのオブジェクトは同じヘッダーで始まるため、汎用 CPython コードは次の方法で未知のオブジェクトを操作できます。`PyObject *`

## 12.2 固定サイズのオブジェクトのレイアウト

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

例:```c
typedef struct {
    PyObject_HEAD
    double value;
} FloatLikeObject;
```記憶の形状:```text
+--------------------+
| ob_refcnt          |
+--------------------+
| ob_type            |
+--------------------+
| value              |
+--------------------+
```このタイプのインスタンスはすべて同じサイズです。

ほとんどが固定サイズのオブジェクト構造体の例:```text
float
function
module
cell
method
weakref
many iterator objects
many descriptor objects
```固定サイズとは、オブジェクトが外部ストレージへの参照を持たないことを意味するものではありません。関数オブジェクトには固定サイズの構造体がありますが、コード オブジェクト、グローバル ディクショナリ、デフォルト タプル、クロージャ タプル、注釈、およびその他のオブジェクトを指します。

## 12.3 可変サイズのオブジェクトのレイアウト

可変サイズ オブジェクトは、サイズ フィールドを使用して共通ヘッダーを拡張します。

概念的には:```c
typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size;
} PyVarObject;
```拡張タイプは以下を使用します。```c
typedef struct {
    PyObject_VAR_HEAD
    PyObject *items[1];
} ArrayLikeObject;
```記憶の形状:```text
+--------------------+
| ob_refcnt          |
+--------------------+
| ob_type            |
+--------------------+
| ob_size            |
+--------------------+
| variable payload   |
+--------------------+

ob_sizeタイプ固有の意味を持ちます。

タイプ サイズフィールドの意味
tuple 要素の数
bytes バイト数
int 内部桁の数と符号
カスタム変数の型 型の実装によって定義される

12.4 インラインストレージと間接ストレージ

可変サイズの Python 値は、データをインラインまたは間接的に保存できます。

タプルは、同じ割り当てに項目参照をインラインで保存します。text tuple object header size = 3 item[0] ---> object A item[1] ---> object B item[2] ---> object C リストには、別の項目配列へのポインターが格納されます。```text list object header size = 3 ob_item ----+ allocated | v [ptr A][ptr B][ptr C][spare...]


タプルは割り当て後にサイズを変更できないため、インライン ストレージが効率的です。

リストは拡大および縮小する必要があるため、リスト オブジェクト自体を移動せずに再割り当てできる別の配列に要素を保持します。

## 12.5 型オブジェクトによるインスタンスのレイアウトの記述

各オブジェクトは型オブジェクトを指します。

タイプ オブジェクトには、次のようなレイアウト メタデータが格納されます。```text
tp_basicsize
tp_itemsize
tp_dictoffset
tp_weaklistoffset
tp_flags
```重要なフィールド:

|フィールド |意味 |
| ------------------- | ------------------------------------- |
|`tp_basicsize`|インスタンスの固定サイズ |
|`tp_itemsize`|各変数末尾項目のサイズ |
|`tp_dictoffset`|インスタンスのオフセット`__dict__`、あれば |
|`tp_weaklistoffset`|サポートされている場合、weakref リストのオフセット |
|`tp_flags`|機能を説明するタイプ フラグ |

固定サイズ型の場合:```text
tp_basicsize = sizeof(MyObject)
tp_itemsize = 0
```可変サイズ型の場合:```text
tp_basicsize = fixed header and fields
tp_itemsize = size of one trailing element
```次に、割り当ては次のように計算します。```text
total bytes = tp_basicsize + n * tp_itemsize
```これは、CPython が長さのタプルを割り当てる方法です。`n`1つのメモリブロック内にあります。

## 12.6 型オブジェクトの動作の説明

型オブジェクトには動作も格納されます。

簡略化したビュー:```text
PyTypeObject
    name
    size fields
    base type
    method table
    member table
    getset table
    deallocator
    repr function
    call function
    attribute functions
    numeric slots
    sequence slots
    mapping slots
    iterator slots
```Python が評価するとき:```python
x + y
```CPython は型スロットを通じてディスパッチします。

Python が評価するとき:```python
x[i]
```CPython はシーケンス スロットまたはマッピング スロットを使用します。

Python が評価するとき:```python
x()
```CPython は呼び出しスロットを使用します。

タイプ オブジェクトは、レイアウト記述子とディスパッチ テーブルの両方です。

## 12.7 メインタイプスロットのカテゴリ`PyTypeObject`多くのフィールドが含まれています。重要なカテゴリは次のとおりです。

|スロットカテゴリ |目的 |
| -------------------- | -------------------------------------------- |
|ライフサイクル スロット |割り当て、初期化、割り当て解除 |
|表現スロット |`repr``str`|
|属性スロット |取得、設定、記述子の動作 |
|コールスロット |関数呼び出し構文 |
|スロット数 |算術演算とビット演算 |
|シーケンススロット |長さ、インデックス付け、包含、連結 |
|スロットのマッピング |辞書形式の検索と割り当て |
|イテレータスロット |反復プロトコル |
|バッファスロット |生メモリの露出 |
| GC スロット |トラバースとクリア |
|スロットのサブクラス化 |継承と MRO の動作 |

各カテゴリは、Python 構文または実行時の動作を C 関数ポインターにマップします。

## 12.8 ライフサイクルスロット

ライフサイクル スロットは、作成と破棄を制御します。

重要なスロット:```text
tp_new
tp_init
tp_alloc
tp_dealloc
tp_free
```一般的な作成パス:```text
call class
    tp_new allocates or returns object
    tp_init initializes object
    return object
```例:```python
obj = MyClass(1, 2)
```概念的には:```text
MyClass.__call__
    MyClass.__new__
    MyClass.__init__
```C レベルでは、これはタイプ スロットを介して流れます。

不変オブジェクトの場合、`tp_new`オブジェクトは作成後に変更できないため、多くの場合、完全な値が構築されます。

可変オブジェクトの場合、`tp_init`割り当て後にフィールドを埋めたりリセットしたりできます。

## 12.9 スロットの割り当て解除`tp_dealloc`参照カウントがゼロになったオブジェクトを破棄します。

単純な割り当て解除機能:```c
static void
Box_dealloc(BoxObject *self)
{
    Py_XDECREF(self->value);
    Py_TYPE(self)->tp_free((PyObject *)self);
}
```割り当て解除機能は、オブジェクトが所有するすべての Python 参照を解放する必要があります。

GC 対応型の場合、デアロケーターは参照を解除する前にオブジェクトの追跡も解除する必要があります。```c
static void
Box_dealloc(BoxObject *self)
{
    PyObject_GC_UnTrack(self);
    Py_CLEAR(self->value);
    Py_TYPE(self)->tp_free((PyObject *)self);
}
```デアロケーターは防御的に作成する必要があります。`Py_DECREF`そして`Py_CLEAR`ファイナライザーを通じて間接的に任意の Python コードを実行できます。

## 12.10 表現スロット

表現スロットのサポート`repr()`そして`str()`

重要なスロット:```text
tp_repr
tp_str
```Python コード:```python
repr(obj)
str(obj)
```型の動作にマップされます。

タイプ実装スケッチの例:```c
static PyObject *
Counter_repr(CounterObject *self)
{
    return PyUnicode_FromFormat("Counter(%ld)", self->value);
}
```それから:```python
repr(counter)
```以下を生成できます:```text
Counter(10)
```もし`tp_str`が存在しない場合、CPython  にフォールバックする可能性があります`tp_repr`またはタイプに応じた一般的なオブジェクトの書式設定動作。

## 12.11 属性アクセススロット

属性アクセスは、CPython の最も重要なパスの 1 つです。

関連するスロット:```text
tp_getattro
tp_setattro
tp_getattr
tp_setattr
```現代のタイプは通常使用します`tp_getattro`そして`tp_setattro`

Python コード:```python
obj.name
obj.name = value
del obj.name
```属性機構を介して流れます。

通常のオブジェクトの場合、汎用属性検索は以下を処理します。```text
data descriptors
instance dictionary
non-data descriptors
class attributes
base classes
__getattr__
```カスタム タイプは、カスタム属性スロットを提供することでこの動作をオーバーライドできます。

## 12.12 記述子スロット

記述子はバインディング動作を実装します。

記述子のタイプでは以下を定義できます。```text
tp_descr_get
tp_descr_set
```Python レベルで同等のもの:```python
__get__
__set__
__delete__
```関数は記述子です。関数がクラスに格納され、インスタンスを通じてアクセスされると、記述子ロジックによってバインドされたメソッドが作成されます。```python
class Counter:
    def inc(self):
        return 1

c = Counter()
m = c.inc
```概念的には:```text
look up inc on Counter
find function object
function descriptor binds self = c
return bound method
```記述子は、メソッド、プロパティ、クラス メソッド、静的メソッド、スロット、および多くの組み込み属性を実装します。

## 12.13 コールスロット

呼び出しスロットは関数呼び出し構文をサポートします。

関連スロット:```text
tp_call
```Python コード:```python
obj(a, b, c)
```オブジェクトの型が呼び出し可能である必要があります。

関数、クラス、メソッド、組み込み関数、およびオブジェクト`__call__`すべてがこのプロトコルに参加します。

ユーザー定義クラスの場合:```python
class F:
    def __call__(self, x):
        return x + 1

f = F()
print(f(10))
```型レベルでは、クラス機構は次のインスタンスを保証します。`__call__`呼び出し可能なオブジェクトとして動作します。

## 12.14 数値スロット

数値スロットは算術演算とビット単位の演算を処理します。

彼らは、`PyNumberMethods`テーブル。

共通の概念スロット:```text
nb_add
nb_subtract
nb_multiply
nb_remainder
nb_power
nb_negative
nb_positive
nb_absolute
nb_bool
nb_invert
nb_lshift
nb_rshift
nb_and
nb_xor
nb_or
nb_int
nb_float
nb_index
```Python 構文:```python
a + b
a - b
a * b
-a
bool(a)
a & b
a << b
```番号プロトコル機構を使用します。

ユーザー定義クラスの場合、次のような特別なメソッド`__add__``__bool__` そして`__index__`これらのスロットに接続されています。

## 12.15 二項演算のディスパッチ

2 つのオペランドには 2 つの型があるため、二項演算は慎重にディスパッチする必要があります。

のために:```python
a + b
```CPython では次のことが考慮されます。```text
left operand type
right operand type
subclass relationships
left slot
right reflected slot
NotImplemented result
```Python レベルのメソッド:```python
__add__
__radd__
__iadd__
```内部の二項演算機構にマップします。

型は返すことができます`NotImplemented`他のオペランドを参加させます。

例:```python
class A:
    def __add__(self, other):
        return NotImplemented

class B:
    def __radd__(self, other):
        return "handled by B"

print(A() + B())
```このディスパッチ動作は Python のデータ モデルの一部であり、型スロットを通じて実装されます。

## 12.16 シーケンススロット

シーケンススロットは`PySequenceMethods`テーブル。

共通の概念スロット:```text
sq_length
sq_concat
sq_repeat
sq_item
sq_ass_item
sq_contains
sq_inplace_concat
sq_inplace_repeat
```Python 構文:```python
len(x)
x[i]
x[i] = value
item in x
x + y
x * n
```シーケンススロットを使用する場合があります。

リスト、タプル、文字列、バイト、および範囲はすべて、内部レイアウトが異なりますが、シーケンスの動作を公開します。

## 12.17 スロットのマッピング

マッピングスロットは`PyMappingMethods`テーブル。

共通の概念スロット:```text
mp_length
mp_subscript
mp_ass_subscript
```Python 構文:```python
len(x)
x[key]
x[key] = value
del x[key]
```マッピング スロットを使用する場合があります。

辞書は主なマッピング タイプです。

を実装するユーザー定義オブジェクト`__getitem__``__setitem__` そして`__len__`タイプ スロットの統合を通じてマッピングのような動作に参加できます。

## 12.18 シーケンスとマッピングのルックアップ

同じ構文は、シーケンスまたはマッピング アクセスを意味する場合があります。```python
x[i]
```リストについては、`i`はインデックスです。

辞書の場合、`i`は鍵です。

タイプによって、操作を解釈する方法が決まります。

概念的には:```text
list.__getitem__(index)
    index must be integer-like

dict.__getitem__(key)
    key may be any hashable object
```C レベルでは、CPython は型のマッピングとシーケンス スロットに基づいてディスパッチします。

## 12.19 イテレータスロット

反復では、型レベルのプロトコル サポートを使用します。

関連するスロット:```text
tp_iter
tp_iternext
```Python コード:```python
for item in obj:
    ...
```おおよそ次のことを意味します:```text
iterator = iter(obj)
while true:
    try:
        item = next(iterator)
    except StopIteration:
        break
```C レベルの場合:```text
tp_iter
    returns iterator object

tp_iternext
    returns next item or signals StopIteration
```イテレータはそれ自体を返します`tp_iter`

コンテナは別のイテレータ オブジェクトを返します。

## 12.20 バッファスロット

バッファー プロトコルは、生のメモリをコピーせずに他のオブジェクトに公開します。

関連するスロットエリア:```text
tp_as_buffer
```などのオブジェクト`bytes``bytearray``memoryview`、配列、および数値拡張タイプが参加できます。

バッファ プロトコルは次の点で重要です。```text
zero-copy I/O
binary parsing
NumPy interop
memoryview
file and socket operations
serialization
```A buffer exporter must keep memory valid while consumers hold views.

これにより、バッファ スロットがオブジェクト モデルとメモリ寿命モデルの両方の一部になります。

## 12.21 ガベージ コレクション スロット

GC 対応のコンテナ タイプには、トラバーサルとクリアのサポートが必要です。

関連するスロット:```text
tp_traverse
tp_clear

tp_traverseレポートにはコレクターへの Python 参照が含まれていました。```c static int Box_traverse(BoxObject *self, visitproc visit, void *arg) { Py_VISIT(self->value); return 0; }


`tp_clear`循環収集中にブレークに参照が含まれていました。```c
static int
Box_clear(BoxObject *self)
{
    Py_CLEAR(self->value);
    return 0;
}
```型がサイクルに参加できるにもかかわらずこれらが正しく実装されていない場合到達不能なサイクルがリークする可能性があります

## 12.22 メンバーテーブル

一部の C 拡張フィールドはメンバー定義を通じて公開できます

概念的なパターン:```c
static PyMemberDef Point_members[] = {
    {"x", T_LONG, offsetof(PointObject, x), 0, "x coordinate"},
    {"y", T_LONG, offsetof(PointObject, y), 0, "y coordinate"},
    {NULL}
};
```type オブジェクトはこのテーブルを指します。```c
.tp_members = Point_members
```これによりCPython  C 構造体フィールドを Python 属性として公開できるようになります

メンバー テーブルは単純な C フィールドに役立ちますより複雑な属性には通常getset 記述子が使用されます

## 12.23 Getset テーブル

Getset テーブルは計算された属性を公開します

概念的なパターン:```c
static PyObject *
Point_get_norm(PointObject *self, void *closure)
{
    double norm = sqrt(self->x * self->x + self->y * self->y);
    return PyFloat_FromDouble(norm);
}

static PyGetSetDef Point_getset[] = {
    {"norm", (getter)Point_get_norm, NULL, "vector norm", NULL},
    {NULL}
};
```type オブジェクトは getset テーブルを指します。```c
.tp_getset = Point_getset
```getset エントリは記述子のように動作します

Python コード:```python
p.norm
```ゲッターを呼び出します

## 12.24 メソッドテーブル

C 拡張メソッドはメソッド テーブルを通じて公開されます

概念的なパターン:```c
static PyObject *
Counter_inc(CounterObject *self, PyObject *Py_UNUSED(ignored))
{
    self->value += 1;
    return PyLong_FromLong(self->value);
}

static PyMethodDef Counter_methods[] = {
    {"inc", (PyCFunction)Counter_inc, METH_NOARGS, "increment counter"},
    {NULL}
};
```type オブジェクトはメソッド テーブルを指します。```c
.tp_methods = Counter_methods
```Python コード:```python
counter.inc()
```記述子とメソッド呼び出し機構を使用して C 関数を呼び出します

## 12.25 ヒープ型と静的型

CPython には静的型とヒープ型があります

静的型は C のグローバル構造体として定義されます多くの組み込み型は歴史的に静的型オブジェクトを使用しています

ヒープ タイプは実行時に動的に割り当てられますユーザー定義の Python クラスはヒープ タイプです

比較:

|タイプの種類 |割り当て |一般的な使用法 |
| ----------- | ------------------ | ------------------------------------------ |
|静的型 | C 静的ストレージ |組み込み型と拡張定義型 |
|ヒープタイプ |ランタイム割り当て | Python クラス多くの最新の拡張タイプ |

ヒープタイプはより柔軟ですこれらは動的属性メタデータのサブクラス化ランタイム管理のライフサイクルなどの通常のクラスの動作をサポートします

最新の拡張コードではモジュール定義スロットを介したヒープ タイプが優先されることが多く、`PyType_FromSpec`。

##12.26`PyType_FromSpec`

`PyType_FromSpec`宣言的な仕様から型オブジェクトを作成します

概念的なスケッチ:```c
static PyType_Slot Counter_slots[] = {
    {Py_tp_dealloc, Counter_dealloc},
    {Py_tp_methods, Counter_methods},
    {0, NULL}
};

static PyType_Spec Counter_spec = {
    .name = "example.Counter",
    .basicsize = sizeof(CounterObject),
    .itemsize = 0,
    .flags = Py_TPFLAGS_DEFAULT,
    .slots = Counter_slots,
};
```それから:```c
PyObject *type = PyType_FromSpec(&Counter_spec);
```このスタイルでは大きなサイズの変数を直接初期化することを回避します。`PyTypeObject`構造化されており進化する CPython 内部でより適切に動作します

## 12.27 スロットの継承

サブクラスはオーバーライドしない限り基本クラスの動作を継承します。```python
class A:
    def __len__(self):
        return 10

class B(A):
    pass

print(len(B()))

B長さの動作を継承しますA

C レベルでは、型の準備が継承されたスロットを埋め、最終的な型レイアウトとメソッド解決メタデータを計算します。

スロットの継承では以下を考慮する必要があります。```text base class layout method resolution order descriptor behavior special method lookup type flags GC support memory offsets


## 12.28 特別なメソッドの検索

特別なメソッド ルックアップは、多くの場合、通常のインスタンス ルックアップをバイパスします。

例:```python
class X:
    pass

x = X()
x.__len__ = lambda: 3

len(x)
```これでもまだ上がる`TypeError`なぜなら`len(x)`インスタンス ディクショナリではなく、型に関する長さの動作を検索します。

正しい:```python
class X:
    def __len__(self):
        return 3

x = X()
print(len(x))
```このルールにより、CPython はプロトコル操作を最適化し、組み込み構文の一貫した動作を維持できます。

## 12.29 レイアウトの競合

多重継承によりレイアウトの競合が発生する可能性があります。

2 つの基本クラスが互換性のない C レベルのインスタンス レイアウトを必要とする場合、CPython はクラスを拒否する可能性があります。

形状の例:```text
BaseA requires C layout A
BaseB requires C layout B
Derived(BaseA, BaseB)
    cannot combine both layouts safely
```Pure Python クラスは、インスタンス状態が辞書ベースまたはスロットベースであるため、通常は柔軟性があります。

C 拡張タイプには、より厳しいレイアウト要件があります。

これが、サブクラス化動作が明示的に必要でない限り、拡張型の設計が継承に関して保守的であるべき理由の 1 つです。

## 12.30 メンタルモデル

型コードを読み取るときにこのモデルを使用します。```text
object memory stores state
type object describes layout and behavior
slots map Python operations to C functions
protocols are implemented through slot tables
attribute lookup uses descriptors and dictionaries
numeric, sequence, and mapping syntax dispatch through specialized slots
GC-aware objects must expose references through traversal slots
deallocation must release owned references and free memory
```Python 式は通常、型スロットを通るパスです。```python
result = obj[key] + other
```概念的には:```text
load obj
dispatch subscription through mapping or sequence slot
load other
dispatch addition through number slots
store result
```## 12.31 概要

オブジェクト レイアウトは、CPython オブジェクトのメモリ形状を定義します。タイプ スロットは、オブジェクトがサポートする操作を定義します。`PyObject`そして`PyVarObject`共通ヘッダーを提供します。具体的な構造体は、型固有のフィールドを追加します。`PyTypeObject`レコード サイズ、オフセット、フラグ、ラ​​イフサイクル フック、属性の動作、プロトコル スロット、メソッド テーブル、メンバー テーブル、getset テーブル、ガベージ コレクションのサポート。

この設計により、CPython は統一されたオブジェクト表現と、組み込み型および拡張型の高度に特殊化された実装を組み合わせることができます。