#
1.4 モジュールのメソッドテーブルと初期化関数

さて、前に約束したように、`spam_system()` Python プログラム からどうやって呼び出すかをこれから示します。まずは、関数名とアドレスを ``メソッドテーブル (method table)'' に列挙する必要があります:

    static PyMethodDef SpamMethods[] = {
        ...
        {"system",  spam_system, METH_VARARGS,
         "Execute a shell command."},
        ...
        {NULL, NULL, 0, NULL}        /* Sentinel */
    };

リスト要素の三つ目のエントリ ("`METH_VARARGS`") に注意してください。 このエントリは、C 関数が使う呼び出し規約をインタプリタに教えるための フラグです。通常この値は"`METH_VARARGS`" か "`METH_VARARGS | METH_KEYWORDS`" のはずです; `0` は旧式の`PyArg_ParseTuple()` の変化形が使われることを 意味します。

"`METH_VARARGS`" だけを使う場合、C 関数は、Python レベルでの引数が `PyArg_ParseTuple()` が受理できるタプルの形式で渡されるもの と想定しなければなりません; この関数についての詳細は下で説明します。

関数にキーワード引数が渡されることになっているのなら、 第三フィールドに`METH_KEYWORDS` ビットをセットできます。 この場合、C 関数は第三引数に "`PyObject *`" を受理するように せねばなりません。このオブジェクトは、キーワード引数の辞書に なります。こうした関数で引数を解釈するには、 `PyArg_ParseTupleAndKeywords()` を使ってください。

メソッドテーブルは、モジュールの初期化関数内でインタプリタに 渡さねばなりません。初期化関数はモジュールの名前を name としたときに `initname()` という名前でなければ ならず、モジュールファイル内で定義されているもののうち、唯一の 非`static` 要素でなければなりません:

    PyMODINIT_FUNC
    initspam(void)
    {
        (void) Py_InitModule("spam", SpamMethods);
    }

PyMODINIT_FUNC は関数の戻り値を `void` になるように宣言し、 プラットフォーム毎に必要とされる、特有のリンク宣言 (linkage declaration) を定義すること、さらに C++ の場合には関数を `extern "C"` に 宣言することに注意してください。

Python プログラムがモジュール `spam` を初めて import するとき、`initspam()` が呼び出されます。 (Python の埋め込みに関するコメントは下記を参照してください。) `initspam()` は `Py_InitModule()` を呼び出して ``モジュールオブジェクト'' を生成し (オブジェクトは`"spam"` を キーとして辞書 `sys.modules` に挿入されます)、第二引数として 与えたメソッドテーブル (`PyMethodDef` 構造体の配列) の情報に 基づいて、組み込み関数オブジェクトを新たなモジュールに挿入していきます。 `Py_InitModule()` は、自らが生成した (この段階ではまだ未使用の) モジュールオブジェクトへのポインタを返します。 `Py_InitModule()` は、モジュールを満足に初期化できなかった場合、 致命的エラーで中断するため、この関数の呼び出し側がエラーをチェックする 必要はありません。

Python を埋め込む場合には、`_PyImport_Inittab` テーブルの エントリ内に `initspam()` がない限り、`initspam()` は自動的には呼び出されません。この問題を解決する最も簡単な方法は、 `Py_Initialize()` や `PyMac_Initialize()` を 呼び出した後に `initspam()` を直接呼び出し、 静的にリンクしておいたモジュールを静的に初期化してしまうというものです:

    int
    main(int argc, char *argv[])
    {
        /* Python インタプリタに argv[0] を渡す */
        Py_SetProgramName(argv[0]);

        /* Python インタプリタを初期化する。必ず必要。 */
        Py_Initialize();

        /* 静的モジュールを追加する */
        initspam();

Python ソース配布物中の Demo/embed/demo.c ファイル内 に例があります。

注意: 単一のプロセス内 (または `fork()` 後の `exec()` が介入していない状態) における複数のインタプリタにおいて、 `sys.module` から エントリを除去したり新たなコンパイル済みモジュールを import したりすると、拡張モジュールによっては問題を生じることがあります。 拡張モジュールの作者は、内部データ構造を初期化する際にはよくよく 用心すべきです。また、`reload()` 関数を拡張モジュールに 対して利用でき、この場合はモジュール初期化関数 (`initspam()`) は呼び出されますが、モジュールが動的にロード可能なオブジェクトファイル (Unixでは .so、Windows では .dll) から読み出された 場合にはモジュールファイルを再読み込みしないので注意してください。

より実質的なモジュール例は、Python ソース配布物に Modules/xxmodule.c という名前で入っています。 このファイルはテンプレートとしても利用できますし、単に例としても 読めます。ソース配布物や Windows にインストールされた Python に入っている modulator.py では、拡張モジュールで実装しなければならない 関数やオブジェクトを宣言し、実装部分を埋めて作成するためのテンプレート を生成できるような、簡単なグラフィカルユーザインタフェースを 提供しています。 このスクリプトはTools/modulator/ ディレクトリにあります; 詳しくはディレクトリ内の README ファイルを参照してください。