13. 組み込みオブジェクトの実装
#13. 組み込みオブジェクトの実装
組み込みオブジェクトは、Python のコア型の背後にある具体的なデータ構造です。これらは、ID、型、参照カウント、サポートされている場合は属性、および型スロットによって定義された動作を持つという意味では、通常の Python オブジェクトです。これらは、ストレージと操作が C で直接実装されているため、特別です。
この章では大まかな地図を示します。後の章では、文字列、リスト、タプル、辞書、セット、数値、関数、モジュール、およびフレームについてさらに詳しく説明します。
13.1 組み込みは型オブジェクトです
などの内蔵タイプlist、dict、 またはintそれ自体は Python オブジェクトです。python print(type(list)) # <class 'type'> print(type(dict)) # <class 'type'> print(type(int)) # <class 'type'> インスタンスはその型オブジェクトを指します。python xs = [1, 2, 3] print(type(xs)) # <class 'list'> C レベルでは:```text
xs ---> PyListObject
ob_refcnt
ob_type ----> PyList_Type
ob_size
ob_item
allocated
## 13.2 組み込みが C で実装される理由
組み込み型は、Python 実行の最もホットなパスにあります。
一般的な操作は次のとおりです。```text
integer arithmetic
string hashing
attribute lookup
dictionary lookup
list append
tuple creation
function calls
iteration
exception creation
```これらの操作が通常の Python コードとして実装された場合、インタープリターは独自の基本操作を実行するためにさらに多くのバイトコードを実行する必要があります。 CPython は、C でコア型を実装することでこれを回避します。
たとえば、Python 辞書は次の目的で使用されます。```text
module globals
class namespaces
object attributes
keyword arguments
import caches
annotations
many user data structures
```辞書が遅いと、インタープリタ全体が遅くなります。
## 13.3 共通の組み込みオブジェクトパターン
ほとんどの組み込みオブジェクト実装は次の形式に従います。```c
typedef struct {
PyObject_HEAD
/* type-specific fields */
} SomeObject;
```または、可変サイズのオブジェクトの場合:```c
typedef struct {
PyObject_VAR_HEAD
/* type-specific fields */
} SomeVarObject;
```次に、型オブジェクトはスロットを提供します。```c
PyTypeObject Some_Type = {
.tp_name = "...",
.tp_basicsize = sizeof(SomeObject),
.tp_dealloc = ...,
.tp_repr = ...,
.tp_as_number = ...,
.tp_as_sequence = ...,
.tp_as_mapping = ...,
.tp_methods = ...,
};
```このパターンは CPython 全体で繰り返されます。
## 13.4 オブジェクトファミリー
主な組み込みオブジェクト ファミリは次のとおりです。
|家族 |例 |主な役割 |
| ----------------------- | ------------------------------------------ | -------------------------------- |
|数値オブジェクト |`int`、`float`、`complex`、`bool`|算術および数値プロトコル |
|テキストおよびバイナリ オブジェクト |`str`、`bytes`、`bytearray`、`memoryview`|テキスト、バイナリデータ、バッファ |
|シーケンスオブジェクト |`list`、`tuple`、`range`|注文されたコレクション |
|オブジェクトのマッピング |`dict`、`mappingproxy`|キーと値のストレージ |
|オブジェクトを設定する |`set`、`frozenset`|ハッシュベースのメンバーシップ |
|呼び出し可能なオブジェクト |関数、メソッド、組み込み関数 |呼び出し |
|ランタイムオブジェクト |モジュール、フレーム、コード、トレースバック |処刑機械 |
|記述子オブジェクト |プロパティ、getset、メンバー、メソッド記述子 |属性の動作 |
|イテレータオブジェクト |リスト反復子、辞書反復子、ジェネレーター |反復 |
|例外オブジェクト |`BaseException`とサブクラス |エラーの伝播 |
各ファミリーは同じオブジェクト モデルを使用しますが、異なる操作に合わせて最適化されます。
## 13.5 整数オブジェクト
パイソン`int`値は任意の精度です。```python
x = 10**100
print(x)
```CPython は整数を次のように格納します`PyLongObject`、可変サイズのオブジェクト。すべての Python 整数に対して 1 つの固定マシン整数を使用するわけではありません。
概念的には:```text
PyLongObject
PyVarObject header
ob_size = number of internal digits, with sign encoded
digits[]
```小さな整数では、内部桁数がほとんど使用されません。大きな整数には多くの桁が使用されます。
これは、Python の整数が C のようにオーバーフローしない理由を説明します。`long`普通の算数では。```python
x = 2**1000
print(x * x)
```コストは整数のサイズとともに増加します。小さな整数の演算は高速です。非常に大きな整数の演算には多精度演算が必要です。
## 13.6 ブールオブジェクト`bool`のサブクラスです`int`。```python
print(isinstance(True, int)) # True
print(True + True) # 2
```正確に 2 つのブール値シングルトン オブジェクトがあります。```python
True
False
```C レベルでは、これらはランタイム所有のオブジェクトです。コードでは、新しいブール インスタンスを構築するのではなく、Python レベルでブール値を真理値によって比較する必要があります。```python
if condition:
...
```C コードは通常、次のようなマクロを使用してブール値を返します。```c
Py_RETURN_TRUE;
Py_RETURN_FALSE;
```## 13.7 浮動小数点オブジェクト
パイソン`float`通常は C の double として実装されます。
概念的には:```text
PyFloatObject
PyObject header
double value
```float オブジェクトは固定サイズです。```python
x = 1.5
y = 2.25
print(x + y)
```浮動小数点演算はプラットフォームの浮動小数点動作に従い、一般的なシステムでは通常 IEEE 754 倍精度になります。
float は、近似の 2 進実数を格納します。小数を正確に表すものではありません。```python
print(0.1 + 0.2)
```驚くべき結果は、Python 固有の算術演算からではなく、バイナリ浮動小数点表現から得られます。
## 13.8 複雑なオブジェクト
パイソン`complex`2 つの浮動小数点値を格納します。```text
PyComplexObject
PyObject header
real double
imag double
```例:```python
z = 1.5 + 2.0j
print(z.real)
print(z.imag)
```複素数は数値スロットに参加します。これらは算術演算をサポートしますが、次のような順序比較はサポートしません。`<`。```python
1 + 2j < 3 + 4j # TypeError
```## 13.9 文字列オブジェクト
パイソン`str`Unicode テキストを保存します。
文字列は不変です。```python
s = "hello"
t = s.upper()
upper()別の文字列を作成します。変異しないs。
CPython の Unicode 実装は、コンパクトなストレージ向けに最適化されています。内部表現では、文字列内の最大のコード ポイントに応じて、異なる要素幅を使用できます。
概念的には:text PyUnicodeObject object header length hash cache kind compact/ascii flags character data 重要な文字列の最適化には次のようなものがあります。```text
cached hash value
compact layout
ASCII fast path
interning for selected strings
specialized Unicode operations
## 13.10 バイトとバイト配列`bytes`不変のバイナリデータです。```python
b = b"hello"
bytearray変更可能なバイナリデータです。```python
buf = bytearray(b"hello")
buf[0] = ord("H")
|タイプ |可変 |主な用途 |
| ----------- | ------: | --------------------- |
|`bytes`| いいえ |不変バイナリデータ |
|`bytearray`| はい |可変バイナリバッファ |
どちらも、0 ~ 255 の範囲の整数に対するシーケンスのようなオブジェクトです。```python
b = b"abc"
print(b[0]) # 97
```## 13.11 オブジェクトのリスト表示
リストは変更可能なシーケンスです。```python
xs = [1, 2, 3]
xs.append(4)
```CPython リスト オブジェクトには、個別に割り当てられたオブジェクト参照の配列へのポインタが格納されます。
概念的には:```text
PyListObject
PyVarObject header
ob_size = logical length
ob_item ----> array of PyObject *
allocated = capacity
```配列にはインライン オブジェクト データではなく参照が格納されます。```text
list
ob_item[0] ---> int object 1
ob_item[1] ---> int object 2
ob_item[2] ---> int object 3
```リストが増加すると割り当てが過剰になります。これにより繰り返します`append`平均して効率的です。
## 13.12 タプルオブジェクト
タプルは不変のシーケンスです。```python
t = (1, 2, 3)
```タプルは、項目参照をタプル割り当てにインラインで格納します。
概念的には:```text
PyTupleObject
PyVarObject header
ob_size = length
ob_item[0]
ob_item[1]
ob_item[2]
```タプルは作成後に長さを変更できません。これにより、インライン ストレージが実用的になります。
タプルの不変性はタプルの参照を指し、必ずしも含まれるオブジェクトの深い可変性を指すわけではありません。```python
t = ([],)
t[0].append(1)
print(t) # ([1],)
```タプルは依然として同じリストを指します。リストが変わりました。
## 13.13 辞書オブジェクト
ディクショナリは、キーを値にマッピングするハッシュ テーブルです。```python
d = {"name": "Ada", "age": 36}
```辞書は、ユーザー コードだけでなく、CPython 全体で使用されます。
彼らは以下を保管します:```text
module globals
class namespaces
instance attributes
keyword arguments
import caches
```dict ルックアップには大まかに次のものが必要です。```text
hash the key
find a matching table slot
compare keys if needed
return associated value
```重要なプロパティ:```text
average O(1) lookup
insertion order preservation
hash-based key storage
resize when table becomes too full
specialized layouts for object attributes
```辞書は、CPython のオブジェクトの中で最もパフォーマンスに依存するものの 1 つです。
## 13.14 Set オブジェクトと Frozenset オブジェクト
セットは、値のないキーのハッシュ テーブルです。```python
seen = set()
seen.add("x")
```フローズンセットは不変です。```python
s = frozenset(["a", "b"])
```セットはメンバーシップ テスト用に最適化されています。```python
if item in seen:
...
```内部構造は精神的に dict に似ていますが、要素のみを保存します。
集合演算には次のものが含まれます。```text
union
intersection
difference
symmetric difference
subset testing
membership testing
frozensetすべての要素がハッシュ可能であれば、ハッシュ可能であるため、辞書キーまたはセット要素として使用できます。
13.15 範囲オブジェクト
あrangeすべての要素を保存せずに等差数列を表します。python r = range(0, 1_000_000, 2) 概念的には:text range object start stop step length オブジェクトは広範囲にわたってもコンパクトです。```python
import sys
print(sys.getsizeof(range(10))) print(sys.getsizeof(range(10**12)))
## 13.16 関数オブジェクト
Python 関数オブジェクトは、実行可能コードとランタイム コンテキストをラップします。```python
def add(a, b):
return a + b
```関数オブジェクトには以下への参照が含まれます。```text
code object
globals dictionary
defaults
keyword defaults
closure cells
annotations
qualified name
module name
```概念的には:```text
PyFunctionObject
code
globals
defaults
kwdefaults
closure
annotations
name
qualname
```コードオブジェクトにはバイトコードが含まれています。関数オブジェクトは、そのバイトコードを実行するために必要な環境を提供します。
## 13.17 コードオブジェクト
コード オブジェクトは、コンパイルされた実行可能メタデータです。
これには以下が含まれます:```text
bytecode
constants
names
local variable names
free variables
cell variables
stack size
flags
line table
exception table
filename
function name
```例:```python
def f(x):
return x + 1
code = f.__code__
print(code.co_consts)
print(code.co_varnames)
```コードオブジェクトは不変です。複数の関数オブジェクトで共有できます。
## 13.18 モジュールオブジェクト
モジュール オブジェクトはインポートされたモジュールを表します。```python
import math
print(math)
```モジュールにはほとんどの場合、名前空間辞書が含まれています。
概念的には:```text
module object
name
dict
spec
loader
package
file
```モジュール ディクショナリには、モジュールによって定義されたグローバル変数が格納されます。```python
import math
print(math.__dict__["sqrt"])
```モジュールをインポートすると、モジュール オブジェクトが作成または取得され、それが保存されます。`sys.modules`。
## 13.19 クラスとインスタンスのオブジェクト
クラスは型オブジェクトです。インスタンスは、その型ポインタがクラスを指すオブジェクトです。```python
class User:
pass
u = User()
```概念的には:```text
User
type object
attributes and methods
base classes
MRO
u
instance object
ob_type ---> User
instance dictionary or slots
```通常のインスタンスは、属性を辞書に保存します。```python
u.name = "Ada"
```と`__slots__`、インスタンスは、選択したフィールドを辞書の代わりに固定オフセットに保存できます。```python
class Point:
__slots__ = ("x", "y")
```これにより、インスタンスあたりのメモリが削減され、一部の属性アクセス パターンが高速化されます。
## 13.20 メソッドオブジェクト
インスタンスを通じて関数にアクセスすると、Python はバインドされたメソッド オブジェクトを作成します。```python
class C:
def f(self):
return 1
c = C()
m = c.f
```バインドされたメソッドには以下が格納されます。```text
function
self object
```概念的には:```text
bound method
__func__ ---> C.f
__self__ ---> c
```電話をかける`m()`パスする`c`最初の引数として。
これが記述子の動作です。関数オブジェクトの記述子スロットがバインディングを実行します。
## 13.21 組み込み関数およびメソッドオブジェクト
一部の呼び出し可能オブジェクトは C で直接実装されます。
例:```python
len
print
dict.get
list.append
```これらは組み込み関数またはメソッド オブジェクトです。これらは C 関数ポインターとメタデータをラップします。
これらは、操作自体の Python バイトコードの実行を回避するため、同等の Python レベルの関数よりも高速です。
次のような組み込みメソッド`list.append`引き続き Python オブジェクトを受け取り、内部的には参照所有権ルールに従います。
## 13.22 イテレータオブジェクト
イテレータオブジェクトの実装`__iter__`そして`__next__`。```python
it = iter([1, 2, 3])
print(next(it))
```リスト反復子には以下が格納されます。```text
reference to list
current index
```dict イテレータには以下が格納されます。```text
reference to dict
iteration position
version or mutation state
```ジェネレーターもイテレーターですが、中断された実行フレームが含まれるため、より複雑になります。
## 13.23 ジェネレーターオブジェクト
ジェネレーター オブジェクトは、中断された関数の実行を表します。```python
def count():
yield 1
yield 2
```電話をかける`count()`関数本体はすぐには実行されません。ジェネレーターオブジェクトを作成します。```python
g = count()
```ジェネレーターは実行状態を保存します。```text
code or frame state
instruction position
locals
evaluation stack
exception state
closed/running state
```それぞれ`next(g)`次まで実行を再開します`yield`または戻る。
ジェネレーターは、オブジェクト モデルをインタープリター フレーム モデルに接続します。
## 13.24 フレームオブジェクト
フレーム オブジェクトは、実行中または一時停止されているコードのブロックを表します。
フレームには次のものが含まれます。```text
code object
globals
builtins
locals
value stack
instruction pointer
exception state
previous frame link where exposed
```フレームは、関数呼び出し、モジュール実行、クラス本体の実行、ジェネレーター、コルーチン、およびトレースバック用に作成されます。
フレーム オブジェクトは次の点で重要です。```text
debuggers
profilers
trace functions
exceptions
inspect module
generators and coroutines
```また、これらは十分に高価であるため、CPython は、一部のパスで必要でない限り、Python に表示される完全なフレーム オブジェクトの具体化を回避するように機能しています。
## 13.25 トレースバック オブジェクト
トレースバック オブジェクトは、例外が伝播された場所を記録します。```python
try:
1 / 0
except ZeroDivisionError as exc:
tb = exc.__traceback__
```トレースバックは次のリンクにリンクします。```text
frame
line number or instruction position
next traceback
```トレースバックはフレームを保持できます。フレームはローカルを保持できます。これは、例外によってラージ オブジェクト グラフを存続させることができることを意味します。
これは、長時間実行されるプログラムで一般的なメモリ保持パターンです。
## 13.26 例外オブジェクト
例外は、から派生した通常のオブジェクトです。`BaseException`。```python
try:
raise ValueError("bad")
except ValueError as exc:
print(exc.args)
```例外オブジェクトには以下を格納できます。```text
args
message data
__cause__
__context__
__traceback__
notes
custom attributes
```例外クラスは通常のクラスですが、例外の伝播はインタープリタに深く統合されています。
## 13.27 記述子オブジェクト
記述子は属性アクセスを制御します。
組み込み記述子オブジェクトの種類には次のものがあります。```text
function descriptors
method descriptors
member descriptors
getset descriptors
wrapper descriptors
property objects
classmethod objects
staticmethod objects
```記述子は次の 1 つ以上を定義します。```python
__get__
__set__
__delete__
```記述子は以下を実装します。```text
methods
properties
slots
C-level members
C-level computed attributes
special method wrappers
```記述子がなければ、Python のメソッド バインディングと属性モデルの柔軟性は大幅に低下します。
## 13.28 Memoryview オブジェクト
あ`memoryview`コピーせずに別のオブジェクトのバッファを公開します。```python
b = bytearray(b"hello")
v = memoryview(b)
```メモリビューはエクスポートされたバッファを維持し、可変性に応じてコードがメモリを読み書きできるようにします。
これは、バイトのようなオブジェクトと拡張モジュールにわたるゼロコピー操作に不可欠です。
Memoryview オブジェクトは、バッファーの有効期間ルールに参加します。エクスポータは、アクティブなビューを無効にする方法でメモリを解放したりサイズ変更したりしてはなりません。
## 13.29 カプセルオブジェクト
カプセルは、Python API を介して安全に交換できるように C ポインターをラップします。
C 拡張機能はカプセルを使用して、ネイティブ ポインターを通常の Python オブジェクトにすることなく公開します。
概念的には:```text
capsule
void *pointer
name
destructor
context
```カプセルは、C 拡張機能の相互運用性に役立ちます。これにより、1 つの拡張モジュールが、別の拡張モジュールがインポートできる C API を公開できるようになります。
## 13.30 オブジェクト実装のトレードオフ
組み込みオブジェクトの実装は、いくつかのプレッシャーのバランスをとります。
|圧力 |効果 |
| ------------- | -------------------------------------------------- |
|スピード |ホット操作用の特殊な C パス |
|メモリ使用量 |コンパクトなレイアウト、共有、インターン、フリーリスト |
|互換性 |安定した Python セマンティクスと C API 動作 |
|デバッグ可能性 |ランタイム チェック、デバッグ ビルド、イントロスペクション フック |
|携帯性 |サポートされているプラットフォームを壊すような仮定は避けてください |
|拡張性 |スロット、プロトコル、サブクラス化のサポート |
|安全性 |参照カウント、GC トラバーサル、エラー処理 |
CPython 実装の詳細の多くは、これらのトレードオフから生まれます。
たとえば、リストの過剰割り当てにより追加速度は向上しますが、余分なメモリが保持される可能性があります。辞書の挿入順序はメモリを消費しますが、便利な言語動作を提供します。参照カウントにより迅速に破棄されますが、サイクル GC と慎重な C API 所有権ルールが必要です。
## 13.31 メンタルモデル
このモデルを使用します。```text
built-in type
C struct for instance layout
PyTypeObject for behavior
slots for protocols
methods for public operations
deallocator for owned references
optional GC traversal for cycles
```組み込み型の実装を読むときは、次のように尋ねます。```text
What does the object store?
Does it own Python references?
Is it fixed-size or variable-size?
Does it use auxiliary memory?
Does it participate in cyclic GC?
What slots does its type object fill?
What operations are hot paths?
What invariants must always hold?
```## 13.32 概要
組み込みオブジェクトは、Python のコア ランタイム値の特殊な C 実装です。これらはすべて、共通ヘッダー、型ポインター、型固有のストレージ、参照所有権規則、型スロットによって定義される動作など、同じオブジェクト モデルに従います。
これらの実装は、ほぼすべての Python プログラムの下に位置するため、最適化されています。リスト、タプル、辞書、文字列、関数、モジュール、フレーム、例外は、単なるライブラリの利便性ではありません。これらはインタプリタの動作部分です。