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

バージョン 2.2 で 新たに追加 された仕様です。

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

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

  2. 属性を参照したり設定したりするときに、特別な 記録のための処理が必要でなく、また参照したり設定した値に対して どんな操作も実行する必要がないこと。

これらの条件は、属性の値や、値が計算されるタイミング、 または格納されたデータがどの程度妥当なものであるかといったことに なんら制約を課すものではないことに注意してください。

PyType_Ready() が呼ばれると、これはそのタイプオブジェクトに 参照されている 3つのテーブルを使って、そのタイプオブジェクトの辞書中に デスクリプタ を作成します。各デスクリプタは、インスタンスオブジェクトの属性に 対するアクセスを制御します。それぞれのテーブルはなくてもかまいません。 もしこれら 3つがすべて NULLだと、その型のインスタンスはその基底型から 継承した属性だけを持つことになります。また、tp_getattro および tp_setattroNULL のままだった場合も、 基底型にこれらの属性の操作がまかせられます。

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

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

tp_methodsNULLでない場合、 これは PyMethodDef 構造体への配列を指している必要があります。 テーブル中の各エントリは、つぎのような構造体のインスタンスです:

typedef struct PyMethodDef {
    char        *ml_name;       /* メソッド名 */
    PyCFunction  ml_meth;       /* 実装する関数 */
    int	         ml_flags;      /* フラグ */
    char        *ml_doc;        /* docstring */
} PyMethodDef;

その型が提供する各メソッドについてひとつのエントリを定義する必要があります。 基底型から継承してきたメソッドについてはエントリは必要ありません。 これの最後には、配列の終わりを示すための見張り番 (sentinel) として 追加のエントリがひとつ必要です。この場合、ml_name メンバが sentinel として使われ、 その値は NULLでなければなりません。

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

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

このテーブルの各エントリに対してデスクリプタが作成され、 値をインスタンスの構造体から抽出しうる型に対してそれらが追加されます。 type メンバは structmember.h ヘッダで定義された 型のコードをひとつ含んでいる必要があります。この値は Python における 値と C における値をどのように変換しあうかを定めるものです。 flags メンバはこの属性がどのようにアクセスされるかを制御する フラグを格納するのに使われます。

以下のフラグ用定数は structmember.h で定義されており、 これらはビットごとの OR を取って組み合わせられます。

Constant Meaning
READONLY 絶対に変更できない。
RO READONLY の短縮形。
READ_RESTRICTED 制限モード (restricted mode) では参照できない。
WRITE_RESTRICTED 制限モード (restricted mode) では変更できない。
RESTRICTED 制限モード (restricted mode) では参照も変更もできない。

tp_members を使ったひとつの面白い利用法は、実行時に使われる デスクリプタを作成しておき、単にテーブル中にテキストを置いておくことによって、 この方法で定義されたすべての属性に doc string を関連付けられるように することです。 アプリケーションはこのイントロスペクション用 API を使って、 クラスオブジェクトからデスクリプタを取り出し、その __doc__ 属性を 使って doc string を得られます。

tp_methods テーブルと同じように、ここでも name メンバの値を NULL にした見張り用エントリが必要です。

ご意見やご指摘をお寄せになりたい方は、 このドキュメントについて... をご覧ください。