拡張の型の定義: 雑多なトピック
******************************

この節ではさまざまな実装可能なタイプメソッドと、それらが何をするもので
あるかについて、ざっと説明します。

以下は "PyTypeObject" の定義です。 デバッグビルド でしか使われないいく
つかのメンバは省いてあります:

   typedef struct _typeobject {
       PyObject_VAR_HEAD
       const char *tp_name; /* 表示用。形式は "<module>.<name>" */
       Py_ssize_t tp_basicsize, tp_itemsize; /* 割り当て用 */

       /* 標準の操作を実装するメソッド */

       destructor tp_dealloc;
       Py_ssize_t tp_vectorcall_offset;
       getattrfunc tp_getattr;
       setattrfunc tp_setattr;
       PyAsyncMethods *tp_as_async; /* 以前の名前は tp_compare (Python 2)
                                       または tp_reserved (Python 3) */
       reprfunc tp_repr;

       /* 標準クラス用のメソッド群 */

       PyNumberMethods *tp_as_number;
       PySequenceMethods *tp_as_sequence;
       PyMappingMethods *tp_as_mapping;

       /* その他の標準の操作 (バイナリ互換性のためこの位置に配置) */

       hashfunc tp_hash;
       ternaryfunc tp_call;
       reprfunc tp_str;
       getattrofunc tp_getattro;
       setattrofunc tp_setattro;

       /* オブジェクトを入出力バッファとして扱う関数 */
       PyBufferProcs *tp_as_buffer;

       /* 任意機能や拡張機能の有無を定義するフラグ */
       unsigned long tp_flags;

       const char *tp_doc; /* ドキュメント文字列 */

       /* リリース 2.0 で意味を割り当て */
       /* アクセス可能な全オブジェクトについて関数を呼び出す */
       traverseproc tp_traverse;

       /* 保持するオブジェクトへの参照を削除する */
       inquiry tp_clear;

       /* リリース 2.1 で意味を割り当て */
       /* 拡張比較 */
       richcmpfunc tp_richcompare;

       /* 弱参照を有効にする */
       Py_ssize_t tp_weaklistoffset;

       /* イテレータ */
       getiterfunc tp_iter;
       iternextfunc tp_iternext;

       /* 属性デスクリプタとサブクラス化に関するもの */
       PyMethodDef *tp_methods;
       PyMemberDef *tp_members;
       PyGetSetDef *tp_getset;
       // ヒープ型では強参照、静的な型では借用参照
       PyTypeObject *tp_base;
       PyObject *tp_dict;
       descrgetfunc tp_descr_get;
       descrsetfunc tp_descr_set;
       Py_ssize_t tp_dictoffset;
       initproc tp_init;
       allocfunc tp_alloc;
       newfunc tp_new;
       freefunc tp_free; /* 低レベルのメモリ解放ルーチン */
       inquiry tp_is_gc; /* PyObject_IS_GC 用 */
       PyObject *tp_bases;
       PyObject *tp_mro; /* メソッド解決順序 */
       PyObject *tp_cache; /* 現在は未使用 */
       void *tp_subclasses;  /* 静的な組み込み型ではインデックス */
       PyObject *tp_weaklist; /* 静的な組み込み型では未使用 */
       destructor tp_del;

       /* 型の属性キャッシュのバージョンタグ。バージョン 2.6 で追加。
        * ゼロならキャッシュは無効であり、初期化が必要。
        */
       unsigned int tp_version_tag;

       destructor tp_finalize;
       vectorcallfunc tp_vectorcall;

       /* この型に関心を持つ型ウォッチャーを表すビット集合 */
       unsigned char tp_watched;

       /* 使用された tp_version_tag 値の数。
        * 属性キャッシュがこの型で無効な場合は、
        * _Py_ATTR_CACHE_UNUSED に設定する (独自の MRO エントリーなどによる)。
        * それ以外の場合は MAX_VERSIONS_PER_CLASS (別の場所で定義) 以下。
        */
       uint16_t tp_versions_used;
   } PyTypeObject;

*たくさんの* メソッドがありますね。でもそんなに心配する必要はありませ
ん。定義したい型があるなら、実装するのはこのうちのごくわずかですむこと
がほとんどです。

すでに予想されているでしょうが、この構造体について入念に見ていき、様々
なハンドラについてより詳しい情報を提供します。 しかしこれらのメンバが
構造体中で定義されている順番は無視します。というのは、これらのメンバの
現れる順序は歴史的な遺産によるものだからです。 多くの場合いちばん簡単
なのは、必要とするメンバがすべて含まれている例をとってきて、新しく作る
型に合わせて値を変更することです。

   const char *tp_name; /* 表示用 */

これは型の名前です。前の章で説明したように、これは色々な場面で現れ、ほ
とんどは診断目的で使われるものです。 それなので、そのような場面で役に
立つであろう名前を選んでください!

   Py_ssize_t tp_basicsize, tp_itemsize; /* 割り当て用 */

これらのフィールドは、この型のオブジェクトが新しく作成されるときにどれ
だけのメモリを割り当てればよいのかをランタイムに指示します。Python に
は可変長の構造体 (文字列やタプルなどを想像してください) に対する組み込
みのサポートがある程度あり、ここで "tp_itemsize" メンバが使われます。
これらについてはあとでふれます。

   const char *tp_doc;

ここには、Python スクリプトが "obj.__doc__" を参照してドキュメント文字
列を取得するときに返す文字列 (またはそのアドレス) を設定します。

では次に、型の基本的なメソッドに進みます。ほとんどの拡張の型がこのメソ
ッドを実装します。


ファイナライズとメモリ解放
==========================

   destructor tp_dealloc;

型のインスタンスの参照カウントがゼロになり、Python インタプリタがそれ
を潰して再利用したくなると、この関数が呼ばれます。解放すべきメモリをそ
の型が保持していたり、それ以外にも実行すべき後処理がある場合は、それら
をここに入れられます。オブジェクトそれ自体もここで解放される必要があり
ます。この関数の例は、以下のようなものです:

   static void
   newdatatype_dealloc(PyObject *op)
   {
       newdatatypeobject *self = (newdatatypeobject *) op;
       free(self->obj_UnderlyingDatatypePtr);
       Py_TYPE(self)->tp_free(self);
   }

型がガベージコレクションをサポートする場合、デストラクタでは、メンバフ
ィールドをクリアする前に "PyObject_GC_UnTrack()" を呼び出す必要があり
ます。

   static void
   newdatatype_dealloc(PyObject *op)
   {
       newdatatypeobject *self = (newdatatypeobject *) op;
       PyObject_GC_UnTrack(op);
       Py_CLEAR(self->other_obj);
       ...
       Py_TYPE(self)->tp_free(self);
   }

メモリ解放関数でひとつ重要なのは、処理待ちの例外にいっさい手をつけない
ことです。なぜなら、解放用の関数は Python インタプリタがスタックを元の
状態に戻すときに呼ばれることが多いからです。そして (通常の関数からの復
帰でなく) 例外のためにスタックが巻き戻されるときは、すでに発生している
例外からメモリ解放関数を守るものはありません。解放用の関数がおこなう動
作が追加の Python のコードを実行してしまうと、それらは例外が発生してい
ることを検知するかもしれません。これはインタプリタが誤解させるエラーを
発生させることにつながります。これを防ぐ正しい方法は、安全でない操作を
実行する前に処理待ちの例外を保存しておき、終わったらそれを元に戻すこと
です。これは "PyErr_Fetch()" および "PyErr_Restore()" 関数を使うことに
よって可能になります:

   static void
   my_dealloc(PyObject *obj)
   {
       MyObject *self = (MyObject *) obj;
       PyObject *cbresult;

       if (self->my_callback != NULL) {
           PyObject *err_type, *err_value, *err_traceback;

           /* 現在の例外の状態を保存する */
           PyErr_Fetch(&err_type, &err_value, &err_traceback);

           cbresult = PyObject_CallNoArgs(self->my_callback);
           if (cbresult == NULL) {
              PyErr_WriteUnraisable(self->my_callback);
           }
           else {
               Py_DECREF(cbresult);
           }

           /* 保存した例外の状態を復元する */
           PyErr_Restore(err_type, err_value, err_traceback);

           Py_DECREF(self->my_callback);
       }
       Py_TYPE(self)->tp_free(self);
   }

注釈:

  メモリ解放関数で安全に行えることには制限があります。 まず、型が
  ("tp_traverse"、"tp_clear" の一方または両方を使って) ガベージコレク
  ションをサポートする場合、"tp_dealloc" が呼ばれる時点で、オブジェク
  トの一部のメンバがすでにクリアされているか、ファイナライズされている
  可能性があります。 また、"tp_dealloc" の中ではオブジェクトの参照カウ
  ントがゼロになっており、不安定な状態にあります。 上の例のような単純
  でないオブジェクトや API の呼び出しは、"tp_dealloc" を再び呼び出し、
  二重解放によるクラッシュを引き起こす可能性があります。Python 3.4 か
  らは、複雑なファイナライズのコードは "tp_dealloc" に置かず、代わりに
  新しく導入された "tp_finalize" という型メソッドを使うことが推奨され
  ています。

  参考: **PEP 442** で新しいファイナライズの仕組みが説明されています。


オブジェクト表現
================

Python では、オブジェクトの文字列表現を生成するのに 2つのやり方があり
ます: "repr()" 関数を使う方法と、 "str()"  関数を使う方法です。
("print()" 関数は単に "str()" を呼び出します。) これらのハンドラはどち
らも省略できます。

   reprfunc tp_repr;
   reprfunc tp_str;

"tp_repr" ハンドラは呼び出されたインスタンスの文字列表現を格納した文字
列オブジェクトを返す必要があります。簡単な例は以下のようなものです:

   static PyObject *
   newdatatype_repr(PyObject *op)
   {
       newdatatypeobject *self = (newdatatypeobject *) op;
       return PyUnicode_FromFormat("Repr-ified_newdatatype{{size:%d}}",
                                   self->obj_UnderlyingDatatypePtr->size);
   }

"tp_repr" ハンドラを指定しない場合、インタープリタはその型の "tp_name"
とオブジェクトを一意に識別する値を使った表現を生成します。

"tp_str" ハンドラと "str()" の関係は、上の "tp_repr" ハンドラと
"repr()" の関係に相当します。つまり、これは Python のコードがオブジェ
クトのインスタンスに対して "str()" を呼び出したときに呼ばれます。この
関数の実装は "tp_repr" ハンドラのそれと非常に似ていますが、得られる文
字列表現は人間が読むことを意図されています。 "tp_str" が指定されていな
い場合、かわりに "tp_repr" ハンドラが使われます。

以下は簡単な例です:

   static PyObject *
   newdatatype_str(PyObject *op)
   {
       newdatatypeobject *self = (newdatatypeobject *) op;
       return PyUnicode_FromFormat("Stringified_newdatatype{{size:%d}}",
                                   self->obj_UnderlyingDatatypePtr->size);
   }


属性を管理する
==============

属性を持つオブジェクトでは、対応する型が属性の解決方法を制御する関数を
提供する必要があります。 属性を定義する場合は取得用の関数が、属性の設
定を許可する場合は設定用の関数が必要です。 属性の削除は特別な場合で、
ハンドラには新しい値として "NULL" を渡します。

Python は属性ハンドラの組を二つサポートしています。 属性をサポートする
型は、どちらか一組の関数だけを実装すれば十分です。 違いは、一方が属性
名を char* で受け取り、もう一方は PyObject* で受け取る点です。 各型は
実装に都合のよい方を使えます。

   getattrfunc  tp_getattr;        /* char * 版 */
   setattrfunc  tp_setattr;
   /* ... */
   getattrofunc tp_getattro;       /* PyObject * 版 */
   setattrofunc tp_setattro;

オブジェクトの属性へのアクセスが常に単純な操作で済む場合 (この後で説明
します)、PyObject* 版の属性管理関数として使える汎用の実装があります。
Python 2.2 以降、型固有の属性ハンドラが実際に必要になることはほとんど
なくなりました。 ただし、新しい汎用の仕組みを使うように更新されていな
い例も数多くあります。


総称的な属性を管理する
----------------------

ほとんどの型は *単純な* 属性を使うだけです。では、どのような属性が単純
だといえるのでしょうか? それが満たすべき条件はごくわずかです:

1. "PyType_Ready()" が呼ばれたとき、すでに属性の名前がわかっていること
   。

2. 属性が取得または設定されたことを記録するための特別な処理も、属性の
   値に応じた処理も不要であること。

これらの条件は、属性の値、値を計算するタイミング、関連するデータの格納
方法には何の制約も課しません。

"PyType_Ready()" は、型オブジェクトが参照する三つのテーブルを使って *
デスクリプタ* を作成し、型オブジェクトの辞書に格納します。 各デスクリ
プタは、インスタンスオブジェクトの一つの属性へのアクセスを制御します。
テーブルはいずれも省略可能です。 三つとも "NULL" の場合、その型のイン
スタンスが持つ属性は基底型から継承したものだけになります。 この場合、
属性の処理を基底型に任せられるように、"tp_getattro" と "tp_setattro"
フィールドも "NULL" にしてください。

テーブルはタイプオブジェクト中の 3つのメンバとして宣言されています:

   struct PyMethodDef *tp_methods;
   struct PyMemberDef *tp_members;
   struct PyGetSetDef *tp_getset;

"tp_methods" が "NULL" でない場合は、"PyMethodDef" 構造体の配列を指す
必要があります。 テーブル内の各エントリーは、次の構造体のインスタンス
です。

   typedef struct PyMethodDef {
       const char  *ml_name;       /* メソッド名 */
       PyCFunction  ml_meth;       /* 実装関数 */
       int          ml_flags;      /* フラグ */
       const char  *ml_doc;        /* ドキュメント文字列 */
   } PyMethodDef;

型が提供するメソッドごとに一つのエントリーを定義します。 基底型から継
承するメソッドのエントリーは不要です。 末尾には、配列の終わりを示す番
兵となるエントリーをもう一つ追加する必要があります。 番兵の "ml_name"
フィールドは "NULL" にしなければなりません。

2番目のテーブルは、インスタンス中に格納されるデータと直接対応づけられ
た属性を定義するのに使います。いくつもの C の原始的な型がサポートされ
ており、アクセスを読み出し専用にも読み書き可能にもできます。このテーブ
ルで使われる構造体は次のように定義されています:

   typedef struct PyMemberDef {
       const char *name;
       int         type;
       Py_ssize_t  offset;
       int         flags;
       const char *doc;
   } PyMemberDef;

テーブルの各エントリーから、インスタンス構造体の値を取り出せる *デスク
リプタ* が作成され、型に追加されます。 "type" フィールドには
"Py_T_INT" や "Py_T_DOUBLE" のような型コードを設定します。 この値は、
Python の値と C の値を相互に変換する方法を決めるために使います。
"flags" フィールドには、属性へのアクセス方法を制御するフラグを格納しま
す。 "Py_READONLY" を設定すると、Python コードからの値の設定を禁止でき
ます。

"tp_members" テーブルから実行時に使うデスクリプタを作成する方法には、
興味深い利点があります。 この方法で定義した属性は、テーブルにテキスト
を指定するだけでドキュメント文字列を関連付けられます。 アプリケーショ
ンはイントロスペクション API を使ってクラスオブジェクトからデスクリプ
タを取得し、その "__doc__" 属性からドキュメント文字列を取得できます。

As with the "tp_methods" table, a sentinel entry with a "ml_name"
value of "NULL" is required.


特定の型に特化した属性の管理
----------------------------

簡単にするため、ここでは char* 版だけを示します。 インターフェースの
char* 版と PyObject* 版の違いは、名前を渡す引数の型だけです。 この例は
、上の汎用の例と実質的に同じ処理を行いますが、Python 2.2 で追加された
汎用の仕組みは使いません。 ハンドラ関数の呼び出し方を説明するので、機
能を拡張する必要がある場合に、何をすべきかが分かるでしょう。

The "tp_getattr" handler is called when the object requires an
attribute look-up.  It is called in the same situations where the
"__getattr__()" method of a class would be called.

以下に例を示します。:

   static PyObject *
   newdatatype_getattr(PyObject *op, char *name)
   {
       newdatatypeobject *self = (newdatatypeobject *) op;
       if (strcmp(name, "data") == 0) {
           return PyLong_FromLong(self->data);
       }

       PyErr_Format(PyExc_AttributeError,
                    "'%.100s' object has no attribute '%.400s'",
                    Py_TYPE(self)->tp_name, name);
       return NULL;
   }

"tp_setattr" ハンドラは、クラスのインスタンスの "__setattr__()" または
"__delattr__()" メソッドが呼ばれる状況で呼び出されます。 属性を削除す
る場合、第 3 引数は "NULL" になります。 次は単に例外を送出する例です。
本当にこれだけを行いたいのであれば、"tp_setattr" ハンドラを "NULL" に
設定してください。

   static int
   newdatatype_setattr(PyObject *op, char *name, PyObject *v)
   {
       PyErr_Format(PyExc_RuntimeError, "Read-only attribute: %s", name);
       return -1;
   }


オブジェクトの比較
==================

   richcmpfunc tp_richcompare;

"tp_richcompare" ハンドラは、比較が必要なときに呼び出されます。
"__lt__()" などの 拡張比較メソッド に相当し、"PyObject_RichCompare()"
と "PyObject_RichCompareBool()" からも呼び出されます。

この関数は、二つの Python オブジェクトと演算子を引数として受け取ります
。 演算子は "Py_EQ"、"Py_NE"、"Py_LE"、"Py_GE"、"Py_LT"、"Py_GT" のい
ずれかです。 指定された演算子で二つのオブジェクトを比較し、比較が成功
した場合は "Py_True" または "Py_False" を返してください。 比較が実装さ
れておらず、もう一方のオブジェクトの比較メソッドを試すべきことを示すに
は "Py_NotImplemented" を、例外を設定した場合は "NULL" を返してくださ
い。

これは内部ポインタのサイズが等しければ等しいと見なすデータ型のサンプル
実装です:

   static PyObject *
   newdatatype_richcmp(PyObject *lhs, PyObject *rhs, int op)
   {
       newdatatypeobject *obj1 = (newdatatypeobject *) lhs;
       newdatatypeobject *obj2 = (newdatatypeobject *) rhs;
       PyObject *result;
       int c, size1, size2;

       /* 両方の引数が newdatatype 型であることを
          確認するコードは省略 */

       size1 = obj1->obj_UnderlyingDatatypePtr->size;
       size2 = obj2->obj_UnderlyingDatatypePtr->size;

       switch (op) {
       case Py_LT: c = size1 <  size2; break;
       case Py_LE: c = size1 <= size2; break;
       case Py_EQ: c = size1 == size2; break;
       case Py_NE: c = size1 != size2; break;
       case Py_GT: c = size1 >  size2; break;
       case Py_GE: c = size1 >= size2; break;
       }
       result = c ? Py_True : Py_False;
       return Py_NewRef(result);
    }


抽象的なプロトコルのサポート
============================

Python はいくつもの *抽象的な* “プロトコル”をサポートしています。これ
らを使用する特定のインターフェイスについては 抽象オブジェクトレイヤ
(Abstract Objects Layer) で解説されています。

A number of these abstract interfaces were defined early in the
development of the Python implementation.  In particular, the number,
mapping, and sequence protocols have been part of Python since the
beginning.  Other protocols have been added over time.  For protocols
which depend on several handler routines from the type implementation,
the older protocols have been defined as optional blocks of handlers
referenced by the type object.  For newer protocols there are
additional slots in the main type object, with a flag bit being set to
indicate that the slots are present and should be checked by the
interpreter.  (The flag bit does not indicate that the slot values are
non-"NULL". The flag may be set to indicate the presence of a slot,
but a slot may still be unfilled.)

   PyNumberMethods   *tp_as_number;
   PySequenceMethods *tp_as_sequence;
   PyMappingMethods  *tp_as_mapping;

オブジェクトを数値、シーケンス、マッピングとして振る舞わせるには、それ
ぞれ C の "PyNumberMethods"、"PySequenceMethods"、"PyMappingMethods"
型の構造体のアドレスを設定します。 これらの構造体に適切な値を設定する
のは、実装する側の責任です。 それぞれの使用例は、Python の配布ソースの
"Objects" ディレクトリで見つけられます。

   hashfunc tp_hash;

この関数は、もし使うことにしたならば、データ型のインスタンスのハッシュ
番号を返すようにします。次のは単純な例です:

   static Py_hash_t
   newdatatype_hash(PyObject *op)
   {
       newdatatypeobject *self = (newdatatypeobject *) op;
       Py_hash_t result;
       result = self->some_size + 32767 * self->some_number;
       if (result == -1) {
           result = -2;
       }
       return result;
   }

"Py_hash_t" は、幅がプラットフォームによって異なる符号付き整数型です。
"tp_hash" が "-1" を返すとエラーを示すため、上の例のように、ハッシュの
計算に成功したときはこの値を返さないよう注意してください。

   ternaryfunc tp_call;

この関数は、その型のインスタンスが「関数として呼び出される」ときに呼ば
れます。たとえばもし "obj1" にそのインスタンスが入っていて、Python ス
クリプトで "obj1('hello')" を実行したとすると、 "tp_call" ハンドラが呼
ばれます。

この関数は 3つの引数をとります:

1. *self* は呼び出しの対象となるデータ型のインスタンスです。 たとえば
   呼び出しが "obj1('hello')" の場合、*self* は "obj1" になります。

2. *args* は呼び出しの引数を格納しているタプルです。ここから引数を取り
   出すには "PyArg_ParseTuple()" を使います。

3. "kwds" は、渡されたキーワード引数の辞書です。 これが "NULL" でなく
   、キーワード引数をサポートする場合は、
   "PyArg_ParseTupleAndKeywords()" で引数を取り出してください。 キーワ
   ード引数をサポートしない場合にこれが "NULL" でなければ、キーワード
   引数をサポートしていないことを示すメッセージとともに "TypeError" を
   送出してください。

以下は "tp_call" の簡易な実装です:

   static PyObject *
   newdatatype_call(PyObject *op, PyObject *args, PyObject *kwds)
   {
       newdatatypeobject *self = (newdatatypeobject *) op;
       PyObject *result;
       const char *arg1;
       const char *arg2;
       const char *arg3;

       if (!PyArg_ParseTuple(args, "sss:call", &arg1, &arg2, &arg3)) {
           return NULL;
       }
       result = PyUnicode_FromFormat(
           "Returning -- value: [%d] arg1: [%s] arg2: [%s] arg3: [%s]\n",
           self->obj_UnderlyingDatatypePtr->size,
           arg1, arg2, arg3);
       return result;
   }

   /* イテレータ */
   getiterfunc tp_iter;
   iternextfunc tp_iternext;

これらの関数はイテレータプロトコルをサポートします。 どちらのハンドラ
も、呼び出し対象のインスタンスを唯一の引数として受け取り、新しい参照を
返します。 エラーの場合は例外を設定し、"NULL" を返してください。
"tp_iter" は Python の "__iter__()" メソッドに、"tp_iternext" は
Python の "__next__()" メソッドに対応します。

Any *iterable* object must implement the "tp_iter" handler, which must
return an *iterator* object.  Here the same guidelines apply as for
Python classes:

* 独立した複数のイテレータをサポートできるコレクション (リストやタプル
  など) では、"tp_iter" を呼び出すたびに新しいイテレータを作成して返す
  必要があります。

* 一度しか反復できないオブジェクト (ファイルオブジェクトのように、通常
  は反復に副作用があるもの) は、自身への新しい参照を返すことで
  "tp_iter" を実装できます。 その場合は "tp_iternext" ハンドラも実装す
  る必要があります。

*イテレータ* オブジェクトは、"tp_iter" と "tp_iternext" の両方を実装す
る必要があります。 イテレータの "tp_iter" ハンドラは、そのイテレータへ
の新しい参照を返します。 "tp_iternext" ハンドラは、反復する次のオブジ
ェクトがあれば、それへの新しい参照を返します。 反復の終わりに達した場
合、"tp_iternext" は例外を設定せずに "NULL" を返しても、"NULL" を返す
*とともに* "StopIteration" を設定しても構いません。 例外を設定しない方
が、パフォーマンスが少しよくなることがあります。 実際にエラーが発生し
た場合は、"tp_iternext" は必ず例外を設定し、"NULL" を返してください。


弱参照(Weak Reference)のサポート
================================

Python の弱参照の実装では、数値のようにパフォーマンスが重要なオブジェ
クトにオーバーヘッドを課さず、どの型でも弱参照の仕組みに参加できるよう
にすることを目指しています。

参考: "weakref" モジュールのドキュメント。

オブジェクトが弱参照をサポートするには、拡張の型が "tp_flags" フィール
ドの "Py_TPFLAGS_MANAGED_WEAKREF" ビットを設定する必要があります。 従
来の "tp_weaklistoffset" フィールドはゼロのままにしてください。

このフラグを設定する場合は、"Py_TPFLAGS_HAVE_GC" も設定するべきです。

具体的には、静的に宣言した型オブジェクトは次のようになります。

   static PyTypeObject TrivialType = {
       PyVarObject_HEAD_INIT(NULL, 0)
       /* ... 簡略化のためほかのメンバは省略 ... */
       .tp_flags = Py_TPFLAGS_MANAGED_WEAKREF | ...,
   };

このほかに必要なのは、"tp_dealloc" で ("PyObject_ClearWeakRefs()" を呼
び出して) 弱参照をすべてクリアすることだけです。

   static void
   Trivial_dealloc(PyObject *op)
   {
       /* デストラクタを呼ぶ前に、まず弱参照をクリアする */
       PyObject_ClearWeakRefs(op);
       /* ... 簡略化のため残りの破棄処理は省略 ... */
       Py_TYPE(op)->tp_free(op);
   }


その他いろいろ
==============

新しいデータ型の特定のメソッドを実装する方法を知るには、*CPython* のソ
ースコードを入手してください。 "Objects" ディレクトリで、"tp_" に目的
の関数名を続けた文字列 ("tp_richcompare" など) を C ソースファイルから
検索すると、実装したい関数の例が見つかります。

オブジェクトが実装中の型のインスタンスであることを確認するには、
"PyObject_TypeCheck()" 関数を使ってください。 例えば、次のように使いま
す。

   if (!PyObject_TypeCheck(some_object, &MyType)) {
       PyErr_SetString(PyExc_TypeError, "arg #1 not a mything");
       return NULL;
   }

参考:

  CPython のソースリリースをダウンロードできます。
     https://www.python.org/downloads/source/

  CPython のソースコードが開発されている GitHub 上の CPython プロジェ
  クトです。
     https://github.com/python/cpython
