1. C や C++ による Python の拡張¶
C プログラムの書き方を知っているなら、Python に新たな組み込みモジュールを追加するのはきわめて簡単です。この新たなモジュール、拡張モジュール (extension module) を使うと、Python が直接行えない二つのこと: 新しい組み込みオブジェクトの実装、そして全ての C ライブラリ関数とシステムコールに対する呼び出し、ができるようになります。
拡張モジュールをサポートするため、Python API (Application Programmer's Interface) では一連の関数、マクロおよび変数を提供していて、Python ランタイムシステムのほとんどの側面へのアクセス手段を提供しています。 Python API は、ヘッダ "Python.h"
をインクルードして C ソースに取り込みます。
拡張モジュールのコンパイル方法は、モジュールの用途やシステムの設定方法に依存します。詳細は後の章で説明します。
注釈
C 拡張のインターフェイスは CPython に固有のものであり、これによる拡張モジュールはほかの Python 実装では動作しません。多くの場合、C 拡張を書くことを避けてほかの Python 実装のために移植性を確保することは可能です。たとえば、あなたがしたいことが C ライブラリの関数やシステムコールを呼び出すことである場合、 ctypes
あるいは cffi ライブラリの利用を検討すべきです。これらのモジュールは C コードとインターフェイスし、C 拡張を書いてコンパイルするのに較べて Python 実装間のより高い移植性をもった Python コードを書かせてくれます。
1.1. 簡単な例¶
spam
(Monty Python ファンの好物ですね) という名の拡張モジュールを作成することにして、C ライブラリ関数 system()
に対する Python インターフェイスを作成したいとします。 [1] この関数は null で終端されたキャラクタ文字列を引数にとり、整数を返します。この関数を以下のようにして Python から呼び出せるようにしたいとします。
>>> import spam
>>> status = spam.system("ls -l")
まずは spammodule.c
を作成するところから始めます。 (伝統として、 spam
という名前のモジュールを作成する場合、モジュールの実装が入った C ファイルを spammodule.c
と呼ぶことになっています; spammify
のように長すぎるモジュール名の場合には、単に spammify.c
にもできます。)
このファイルの最初の 2 行は以下のようにします:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
これで、Python API を取り込みます (必要なら、モジュールの用途に関する説明や、著作権表示を追加します)。
注釈
Python は、システムによっては標準ヘッダの定義に影響するようなプリプロセッサ定義を行っているので、 Python.h
をいずれの標準ヘッダよりも前にインクルード せねばなりません 。
#define PY_SSIZE_T_CLEAN
は、いくつかの API では int
の代わりに Py_ssize_t
が使われるべきであることを示すために使われていました。これは Python 3.13 以降では不要となりましたが、後方互換性のために維持されています。このマクロの説明は 文字列とバッファ にあります。
Python.h
で定義されているユーザから可視のシンボルは、全て接頭辞 Py
または PY
が付いています。ただし、標準ヘッダファイル内の定義は除きます。簡単のためと、Python 内で広範に使うことになるという理由から、 "Python.h"
はいくつかの標準ヘッダファイル: <stdio.h>
、 <string.h>
、 <errno.h>
、および <stdlib.h>
をインクルードしています。後者のヘッダファイルがシステム上になければ、 "Python.h"
が関数 malloc()
、 free()
および realloc()
を直接定義します。
次にファイルに追加する内容は、Python 式 spam.system(string)
を評価する際に呼び出されることになる C 関数です (この関数を最終的にどのように呼び出すかは、後ですぐわかります):
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
return PyLong_FromLong(sts);
}
ここでは、Python の引数リスト (例えば、単一の式 "ls -l"
) から C 関数に渡す引数にそのまま変換しています。 C 関数は常に二つの引数を持ち、便宜的に self および args と呼ばれます。
self 引数には、モジュールレベルの関数であればモジュールが、メソッドにはオブジェクトインスタンスが渡されます。
args 引数は、引数の入った Python タプルオブジェクトへのポインタになります。タプル内の各要素は、呼び出しの際の引数リストにおける各引数に対応します。引数は Python オブジェクトです --- C 関数で引数を使って何かを行うには、オブジェクトから C の値に変換せねばなりません。Python API の関数 PyArg_ParseTuple()
は引数の型をチェックし、C の値に変換します。 PyArg_ParseTuple()
はテンプレート文字列を使って、引数オブジェクトの型と、変換された値を入れる C 変数の型を判別します。これについては後で詳しく説明します。
PyArg_ParseTuple()
は、全ての引数が正しい型を持っていて、アドレス渡しされた各変数に各引数要素を保存したときに真 (非ゼロ) を返します。この関数は不正な引数リストを渡すと偽 (ゼロ) を返します。後者の場合、関数は適切な例外を送出するので、呼び出し側は (例にもあるように) すぐに NULL
を返すようにしてください。
1.2. 幕間小話: エラーと例外¶
An important convention throughout the Python interpreter is the following: when
a function fails, it should set an exception condition and return an error value
(usually -1
or a NULL
pointer). Exception information is stored in
three members of the interpreter's thread state. These are NULL
if
there is no exception. Otherwise they are the C equivalents of the members
of the Python tuple returned by sys.exc_info()
. These are the
exception type, exception instance, and a traceback object. It is important
to know about them to understand how errors are passed around.
Python API では、様々な型の例外をセットするための関数をいくつか定義しています。
もっともよく用いられるのは PyErr_SetString()
です。引数は例外オブジェクトと C 文字列です。例外オブジェクトは通常、 PyExc_ZeroDivisionError
のような定義済みのオブジェクトです。 C 文字列はエラーの原因を示し、Python 文字列オブジェクトに変換されて例外の "付属値" に保存されます。
もう一つ有用な関数として PyErr_SetFromErrno()
があります。この関数は引数に例外だけをとり、付属値はグローバル変数 errno
から構築します。もっとも汎用的な関数は PyErr_SetObject()
で、二つのオブジェクト、例外と付属値を引数にとります。これら関数に渡すオブジェクトには Py_INCREF()
を使う必要はありません。
例外がセットされているかどうかは、 PyErr_Occurred()
を使って非破壊的に調べられます。この関数は現在の例外オブジェクトを返します。例外が発生していない場合には NULL
を返します。通常は、関数の戻り値からエラーが発生したかを判別できるはずなので、 PyErr_Occurred()
を呼び出す必要はありません。
関数 g を呼び出す f が、前者の関数の呼び出しに失敗したことを検出すると、 f 自体はエラー値 (大抵は NULL
や -1
) を返さねばなりません。しかし、 PyErr_*
関数群のいずれかを呼び出す必要は ありません --- なぜなら、 g がすでに呼び出しているからです。次いで f を呼び出したコードもエラーを示す値を 自らを呼び出したコード に返すことになりますが、同様に PyErr_*
は 呼び出しません 。以下同様に続きます --- エラーの最も詳しい原因は、最初にエラーを検出した関数がすでに報告しているからです。エラーが Python インタプリタのメインループに到達すると、現在実行中の Python コードは一時停止し、 Python プログラマが指定した例外ハンドラを探し出そうとします。
(モジュールが PyErr_*
関数をもう一度呼び出して、より詳細なエラーメッセージを提供するような状況があります。このような状況ではそうすべきです。とはいえ、一般的な規則としては、この関数を何度も呼び出す必要はなく、ともすればエラーの原因に関する情報を失う結果になりがちです: これにより、ほとんどの操作が様々な理由から失敗するかもしれません)
ある関数呼び出しでの処理の失敗によってセットされた例外を無視するには、 PyErr_Clear()
を呼び出して例外状態を明示的に消去しなくてはなりません。エラーをインタプリタには渡したくなく、自前で (何か他の作業を行ったり、何も起こらなかったかのように見せかけるような) エラー処理を完全に行う場合にのみ、 PyErr_Clear()
を呼び出すようにすべきです。
malloc()
の呼び出し失敗は、常に例外にしなくてはなりません --- malloc()
(または realloc()
) を直接呼び出しているコードは、 PyErr_NoMemory()
を呼び出して、失敗を示す値を返さねばなりません。オブジェクトを生成する全ての関数 (例えば PyLong_FromLong()
) は PyErr_NoMemory()
の呼び出しを済ませてしまうので、この規則が関係するのは直接 malloc()
を呼び出すコードだけです。
また、 PyArg_ParseTuple()
という重要な例外を除いて、整数の状態コードを返す関数はたいてい、Unix のシステムコールと同じく、処理が成功した際にはゼロまたは正の値を返し、失敗した場合には -1
を返します。
最後に、エラー標示値を返す際に、(エラーが発生するまでに既に生成してしまったオブジェクトに対して Py_XDECREF()
や Py_DECREF()
を呼び出して) ごみ処理を注意深く行ってください!
The choice of which exception to raise is entirely yours. There are predeclared
C objects corresponding to all built-in Python exceptions, such as
PyExc_ZeroDivisionError
, which you can use directly. Of course, you
should choose exceptions wisely --- don't use PyExc_TypeError
to mean
that a file couldn't be opened (that should probably be PyExc_OSError
).
If something's wrong with the argument list, the PyArg_ParseTuple()
function usually raises PyExc_TypeError
. If you have an argument whose
value must be in a particular range or must satisfy other conditions,
PyExc_ValueError
is appropriate.
モジュール固有の新たな例外も定義できます。定義するには、通常はファイルの先頭部分に静的なオブジェクト変数の宣言を行います:
static PyObject *SpamError;
and initialize it in your module's initialization function (PyInit_spam()
)
with an exception object:
PyMODINIT_FUNC
PyInit_spam(void)
{
PyObject *m;
m = PyModule_Create(&spammodule);
if (m == NULL)
return NULL;
SpamError = PyErr_NewException("spam.error", NULL, NULL);
if (PyModule_AddObjectRef(m, "error", SpamError) < 0) {
Py_CLEAR(SpamError);
Py_DECREF(m);
return NULL;
}
return m;
}
Note that the Python name for the exception object is spam.error
. The
PyErr_NewException()
function may create a class with the base class
being Exception
(unless another class is passed in instead of NULL
),
described in 組み込み例外.
Note also that the SpamError
variable retains a reference to the newly
created exception class; this is intentional! Since the exception could be
removed from the module by external code, an owned reference to the class is
needed to ensure that it will not be discarded, causing SpamError
to
become a dangling pointer. Should it become a dangling pointer, C code which
raises the exception could cause a core dump or other unintended side effects.
We discuss the use of PyMODINIT_FUNC
as a function return type later in this
sample.
The spam.error
exception can be raised in your extension module using a
call to PyErr_SetString()
as shown below:
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < 0) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
}
1.3. 例に戻る¶
先ほどの関数の例に戻ると、今度は以下の実行文を理解できるはずです:
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
It returns NULL
(the error indicator for functions returning object pointers)
if an error is detected in the argument list, relying on the exception set by
PyArg_ParseTuple()
. Otherwise the string value of the argument has been
copied to the local variable command
. This is a pointer assignment and
you are not supposed to modify the string to which it points (so in Standard C,
the variable command
should properly be declared as const char
*command
).
次の文では、 PyArg_ParseTuple()
で得た文字列を渡して Unix 関数 system()
を呼び出しています:
sts = system(command);
Our spam.system()
function must return the value of sts
as a
Python object. This is done using the function PyLong_FromLong()
.
return PyLong_FromLong(sts);
上の場合では、整数オブジェクトを返します。(そう、整数ですら、 Python においてはヒープ上のオブジェクトなのです! )
何ら有用な値を返さない関数 (void を返す関数) に対応する Python の関数は None
を返さねばなりません。関数に None
を返させるには、以下のような慣用句を使います (この慣用句は Py_RETURN_NONE
マクロに実装されています):
Py_INCREF(Py_None);
return Py_None;
Py_None
は特殊な Pyhton オブジェクトである None
に対応する C での名前です。これまで見てきたようにほとんどのコンテキストで "エラー" を意味する NULL
ポインタとは違い、 None
は純粋な Python のオブジェクトです。
1.4. モジュールのメソッドテーブルと初期化関数¶
I promised to show how spam_system()
is called from Python programs.
First, we need to list its name and address in a "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()
が受理できるタプルの形式で渡されるものと想定しなければなりません; この関数についての詳細は下で説明します。
The METH_KEYWORDS
bit may be set in the third field if keyword
arguments should be passed to the function. In this case, the C function should
accept a third PyObject *
parameter which will be a dictionary of keywords.
Use PyArg_ParseTupleAndKeywords()
to parse the arguments to such a
function.
メソッドテーブルはモジュール定義の構造体から参照されていなければなりません:
static struct PyModuleDef spammodule = {
PyModuleDef_HEAD_INIT,
"spam", /* name of module */
spam_doc, /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
SpamMethods
};
This structure, in turn, must be passed to the interpreter in the module's
initialization function. The initialization function must be named
PyInit_name()
, where name is the name of the module, and should be the
only non-static
item defined in the module file:
PyMODINIT_FUNC
PyInit_spam(void)
{
return PyModule_Create(&spammodule);
}
Note that PyMODINIT_FUNC
declares the function as PyObject *
return type,
declares any special linkage declarations required by the platform, and for C++
declares the function as extern "C"
.
When the Python program imports module spam
for the first time,
PyInit_spam()
is called. (See below for comments about embedding Python.)
It calls PyModule_Create()
, which returns a module object, and
inserts built-in function objects into the newly created module based upon the
table (an array of PyMethodDef
structures) found in the module definition.
PyModule_Create()
returns a pointer to the module object
that it creates. It may abort with a fatal error for
certain errors, or return NULL
if the module could not be initialized
satisfactorily. The init function must return the module object to its caller,
so that it then gets inserted into sys.modules
.
When embedding Python, the PyInit_spam()
function is not called
automatically unless there's an entry in the PyImport_Inittab
table.
To add the module to the initialization table, use PyImport_AppendInittab()
,
optionally followed by an import of the module:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int
main(int argc, char *argv[])
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
/* Add a built-in module, before Py_Initialize */
if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
fprintf(stderr, "Error: could not extend in-built modules table\n");
exit(1);
}
/* Pass argv[0] to the Python interpreter */
status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
if (PyStatus_Exception(status)) {
goto exception;
}
/* Initialize the Python interpreter. Required.
If this step fails, it will be a fatal error. */
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
goto exception;
}
PyConfig_Clear(&config);
/* Optionally import the module; alternatively,
import can be deferred until the embedded script
imports it. */
PyObject *pmodule = PyImport_ImportModule("spam");
if (!pmodule) {
PyErr_Print();
fprintf(stderr, "Error: could not import module 'spam'\n");
}
// ... use Python C API here ...
return 0;
exception:
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
注釈
単一のプロセス内 (または fork()
後の exec()
が介入していない状態) における複数のインタプリタにおいて、 sys.modules
からエントリを除去したり新たなコンパイル済みモジュールを import したりすると、拡張モジュールによっては問題を生じることがあります。拡張モジュールの作者は、内部データ構造を初期化する際にはよくよく用心すべきです。
より実質的なモジュール例は、Python ソース配布物に Modules/xxmodule.c
という名前で入っています。このファイルはテンプレートとしても利用できますし、単に例としても読めます。
注釈
xmodule
は spam
と異なり、 多段階初期化 (multi-phase initialization (Python 3.5 の新機能) を使っています。 PyInit_spam
が PyModuleDef を返し、モジュールの生成は後に import 機構が行います。多段階初期化についての詳細は PEP 489 を参照してください。
1.5. コンパイルとリンク¶
新しい拡張モジュールを使えるようになるまで、まだ二つの作業: コンパイルと、Python システムへのリンク、が残っています。動的読み込み (dynamic loading) を使っているのなら、作業の詳細は自分のシステムが使っている動的読み込みの形式によって変わるかもしれません; 詳しくは、拡張モジュールのビルドに関する章 (C および C++ 拡張のビルド 章) や、Windows におけるビルドに関係する追加情報の章 (Windows 上での C および C++ 拡張モジュールのビルド 章) を参照してください。
動的読み込みを使えなかったり、モジュールを常時 Python インタプリタの一部にしておきたい場合には、インタプリタのビルド設定を変更して再ビルドしなければならなくなるでしょう。Unixでは、幸運なことにこの作業はとても単純です: 単に自作のモジュールファイル (例えば spammodule.c
) を展開したソース配布物の Modules/
ディレクトリに置き、 Modules/Setup.local
に自分のファイルを説明する以下の一行:
spam spammodule.o
を追加して、トップレベルのディレクトリで make を実行して、インタプリタを再ビルドするだけです。 Modules/
サブディレクトリでも make を実行できますが、前もって 'make Makefile' を実行して Makefile
を再ビルドしておかなければならりません。(この作業は Setup
ファイルを変更するたびに必要です。)
モジュールが別のライブラリとリンクされている必要がある場合、ライブラリも設定ファイルに列挙できます。例えば以下のようにします。
spam spammodule.o -lX11
1.6. C から Python 関数を呼び出す¶
これまでは、Python からの C 関数の呼び出しに重点を置いて述べてきました。ところでこの逆: C からの Python 関数の呼び出しもまた有用です。とりわけ、いわゆる "コールバック" 関数をサポートするようなライブラリを作成する際にはこの機能が便利です。ある C インターフェースがコールバックを利用している場合、同等の機能を提供する Python コードでは、しばしば Python プログラマにコールバック機構を提供する必要があります; このとき実装では、 C で書かれたコールバック関数から Python で書かれたコールパック関数を呼び出すようにする必要があるでしょう。もちろん、他の用途も考えられます。
幸運なことに、Python インタプリタは簡単に再帰呼び出しでき、 Python 関数を呼び出すための標準インターフェースもあります。 (Python パーザを特定の入力文字を使って呼び出す方法について詳説するつもりはありません --- この方法に興味があるなら、 Python ソースコードの Modules/main.c
にある、コマンドラインオプション -c
の実装を見てください)
Python 関数の呼び出しは簡単です。まず、C のコードに対してコールバックを登録しようとする Python プログラムは、何らかの方法で Python の関数オブジェクトを渡さねばなりません。このために、コールバック登録関数 (またはその他のインターフェース) を提供せねばなりません。このコールバック登録関数が呼び出された際に、引き渡された Python 関数オブジェクトへのポインタをグローバル変数に --- あるいは、どこか適切な場所に --- 保存します (関数オブジェクトを Py_INCREF()
するようよく注意してください!)。例えば、以下のような関数がモジュールの一部になっていることでしょう:
static PyObject *my_callback = NULL;
static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
PyObject *result = NULL;
PyObject *temp;
if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
if (!PyCallable_Check(temp)) {
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
return NULL;
}
Py_XINCREF(temp); /* Add a reference to new callback */
Py_XDECREF(my_callback); /* Dispose of previous callback */
my_callback = temp; /* Remember new callback */
/* Boilerplate to return "None" */
Py_INCREF(Py_None);
result = Py_None;
}
return result;
}
This function must be registered with the interpreter using the
METH_VARARGS
flag; this is described in section モジュールのメソッドテーブルと初期化関数. The
PyArg_ParseTuple()
function and its arguments are documented in section
拡張モジュール関数でのパラメタ展開.
Py_XINCREF()
および Py_XDECREF()
は、オブジェクトに対する参照カウントをインクリメント/デクリメントするためのマクロで、 NULL
ポインタが渡されても安全に操作できる形式です (とはいえ、上の流れでは temp が NULL
になることはありません)。これらのマクロと参照カウントについては、 参照カウント法 で説明しています。
その後、コールバック関数を呼び出す時が来たら、C 関数 PyObject_CallObject()
を呼び出します。この関数には二つの引数: Python 関数と Python 関数の引数リストがあり、いずれも任意の Python オブジェクトを表すポインタ型です。引数リストは常にタプルオブジェクトでなければならず、その長さは引数の数になります。Python 関数を引数なしで呼び出すのなら、 NULL
か空のタプルを渡します; 単一の引数で関数を呼び出すのなら、単要素 (singleton) のタプルを渡します。 Py_BuildValue()
の書式文字列中に、ゼロ個または一個以上の書式化コードが入った丸括弧がある場合、この関数はタプルを返します。以下に例を示します:
int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
PyObject_CallObject()
は Python オブジェクトへのポインタを返します: これは Python 関数からの戻り値になります。 PyObject_CallObject()
は、引数に対して "参照カウント中立 (reference-count- neutral)" です。上の例ではタプルを生成して引数リストとして提供しており、このタプルは PyObject_CallObject()
の呼び出し直後に Py_DECREF()
されています。
PyObject_CallObject()
は戻り値として "新しい" オブジェクト: 新規に作成されたオブジェクトか、既存のオブジェクトの参照カウントをインクリメントしたものを返します。従って、このオブジェクトをグローバル変数に保存したいのでないかぎり、たとえこの戻り値に興味がなくても (むしろ、そうであればなおさら!) 何がしかの方法で戻り値オブジェクトを Py_DECREF()
しなければなりません。
とはいえ、戻り値を Py_DECREF()
する前には、値が NULL
でないかチェックしておくことが重要です。もし NULL
なら、呼び出した Python 関数は例外を送出して終了させられています。 PyObject_CallObject()
を呼び出しているコード自体もまた Python から呼び出されているのであれば、今度は C コードが自分を呼び出している Python コードにエラー標示値を返さねばなりません。それにより、インタプリタはスタックトレースを出力したり、例外を処理するための Python コードを呼び出したりできます。例外の送出が不可能だったり、したくないのなら、 PyErr_Clear()
を呼んで例外を消去しておかねばなりません。例えば以下のようにします:
if (result == NULL)
return NULL; /* Pass error back */
...use result...
Py_DECREF(result);
Python コールバック関数をどんなインターフェースにしたいかによっては、引数リストを PyObject_CallObject()
に与えなければならない場合もあります。あるケースでは、コールバック関数を指定したのと同じインターフェースを介して、引数リストも渡されているかもしれません。また別のケースでは、新しいタプルを構築して引数リストを渡さねばならないかもしれません。この場合最も簡単なのは Py_BuildValue()
を呼ぶやり方です。例えば、整数のイベントコードを渡したければ、以下のようなコードを使うことになるでしょう:
PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
Py_DECREF(arglist)
が呼び出しの直後、エラーチェックよりも前に置かれていることに注意してください! また、厳密に言えば、このコードは完全ではありません: Py_BuildValue()
はメモリ不足におちいるかもしれず、チェックしておくべきです。
通常の引数とキーワード引数をサポートする PyObject_Call()
を使って、キーワード引数を伴う関数呼び出しをすることができます。上の例と同じように、 Py_BuildValue()
を作って辞書を作ります。
PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);
1.7. 拡張モジュール関数でのパラメタ展開¶
PyArg_ParseTuple()
関数は以下のように宣言されています:
int PyArg_ParseTuple(PyObject *arg, const char *format, ...);
引数 arg は C 関数から Python に渡される引数リストが入ったタプルオブジェクトでなければなりません。 format 引数は書式文字列で、 Python/C API リファレンスマニュアルの 引数の解釈と値の構築 で解説されている書法に従わねばなりません。残りの引数は、それぞれの変数のアドレスで、書式化文字列から決まる型になっていなければなりません。
PyArg_ParseTuple()
は Python 側から与えられた引数が必要な型になっているか調べるのに対し、 PyArg_ParseTuple()
は呼び出しの際に渡された C 変数のアドレスが有効な値を持つか調べられないことに注意してください: ここで間違いを犯すと、コードがクラッシュするかもしれませんし、少なくともでたらめなビットをメモリに上書きしてしまいます。慎重に!
呼び出し側に提供されるオブジェクトへの参照はすべて 借用 参照 (borrowed reference) になります; これらのオブジェクトの参照カウントをデクリメントしてはなりません!
以下にいくつかの呼び出し例を示します:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;
ok = PyArg_ParseTuple(args, ""); /* No arguments */
/* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
/* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
/* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
/* A pair of ints and a string, whose size is also returned */
/* Possible Python call: f((1, 2), 'three') */
{
const char *file;
const char *mode = "r";
int bufsize = 0;
ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
/* A string, and optionally another string and an integer */
/* Possible Python calls:
f('spam')
f('spam', 'w')
f('spam', 'wb', 100000) */
}
{
int left, top, right, bottom, h, v;
ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
&left, &top, &right, &bottom, &h, &v);
/* A rectangle and a point */
/* Possible Python call:
f(((0, 0), (400, 300)), (10, 10)) */
}
{
Py_complex c;
ok = PyArg_ParseTuple(args, "D:myfunction", &c);
/* a complex, also providing a function name for errors */
/* Possible Python call: myfunction(1+2j) */
}
1.8. 拡張モジュール関数のキーワードパラメタ¶
PyArg_ParseTupleAndKeywords()
は、以下のように宣言されています:
int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
const char *format, char * const *kwlist, ...);
arg と format パラメタは PyArg_ParseTuple()
のものと同じです。 kwdict パラメタはキーワード引数の入った辞書で、 Python ランタイムシステムから第三パラメタとして受け取ります。 kwlist パラメタは各パラメタを識別するための文字列からなる、 NULL
終端されたリストです; 各パラメタ名は format 中の型情報に対して左から右の順に照合されます。成功すると PyArg_ParseTupleAndKeywords()
は真を返し、それ以外の場合には適切な例外を送出して偽を返します。
注釈
キーワード引数を使っている場合、タプルは入れ子にして使えません! kwlist 内に存在しないキーワードパラメタが渡された場合、 TypeError
の送出を引き起こします。
以下にキーワードを使ったモジュール例を示します。これは Geoff Philbrick (philbrick@hks.com) によるプログラム例をもとにしています:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
int voltage;
const char *state = "a stiff";
const char *action = "voom";
const char *type = "Norwegian Blue";
static char *kwlist[] = {"voltage", "state", "action", "type", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
&voltage, &state, &action, &type))
return NULL;
printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
action, voltage);
printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);
Py_RETURN_NONE;
}
static PyMethodDef keywdarg_methods[] = {
/* The cast of the function is necessary since PyCFunction values
* only take two PyObject* parameters, and keywdarg_parrot() takes
* three.
*/
{"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
"Print a lovely skit to standard output."},
{NULL, NULL, 0, NULL} /* sentinel */
};
static struct PyModuleDef keywdargmodule = {
PyModuleDef_HEAD_INIT,
"keywdarg",
NULL,
-1,
keywdarg_methods
};
PyMODINIT_FUNC
PyInit_keywdarg(void)
{
return PyModule_Create(&keywdargmodule);
}
1.9. 任意の値を構築する¶
Py_BuildValue()
は PyArg_ParseTuple()
の対極に位置するものです。この関数は以下のように定義されています:
PyObject *Py_BuildValue(const char *format, ...);
Py_BuildValue()
は、 PyArg_ParseTuple()
の認識する一連の書式単位に似た書式単位を認識します。ただし (関数への出力ではなく、入力に使われる) 引数はポインタではなく、ただの値でなければなりません。 Python から呼び出された C 関数が返す値として適切な、新たな Python オブジェクトを返します。
PyArg_ParseTuple()
とは一つ違う点があります: PyArg_ParseTuple()
は第一引数をタプルにする必要があります (Python の引数リストは内部的には常にタプルとして表現されるからです) が、 Py_BuildValue()
はタプルを生成するとは限りません。 Py_BuildValue()
は書式文字列中に書式単位が二つかそれ以上入っている場合にのみタプルを構築します。書式文字列が空なら、 None
を返します。きっかり一つの書式単位なら、その書式単位が記述している何らかのオブジェクトになります。サイズが 0 や 1 のタプル返させたいのなら、書式文字列を丸括弧で囲います。
以下に例を示します (左に呼び出し例を、右に構築される Python 値を示します):
Py_BuildValue("") None
Py_BuildValue("i", 123) 123
Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)
Py_BuildValue("s", "hello") 'hello'
Py_BuildValue("y", "hello") b'hello'
Py_BuildValue("ss", "hello", "world") ('hello', 'world')
Py_BuildValue("s#", "hello", 4) 'hell'
Py_BuildValue("y#", "hello", 4) b'hell'
Py_BuildValue("()") ()
Py_BuildValue("(i)", 123) (123,)
Py_BuildValue("(ii)", 123, 456) (123, 456)
Py_BuildValue("(i,i)", 123, 456) (123, 456)
Py_BuildValue("[i,i]", 123, 456) [123, 456]
Py_BuildValue("{s:i,s:i}",
"abc", 123, "def", 456) {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))
1.10. 参照カウント法¶
C や C++のような言語では、プログラマはヒープ上のメモリを動的に確保したり解放したりする責任があります。こうした作業は C では関数 malloc()
や free()
で行います。C++では本質的に同じ意味で演算子 new
や delete
が使われます。そこで、以下の議論は C の場合に限定して行います。
Every block of memory allocated with malloc()
should eventually be
returned to the pool of available memory by exactly one call to free()
.
It is important to call free()
at the right time. If a block's address
is forgotten but free()
is not called for it, the memory it occupies
cannot be reused until the program terminates. This is called a memory
leak. On the other hand, if a program calls free()
for a block and then
continues to use the block, it creates a conflict with reuse of the block
through another malloc()
call. This is called using freed memory.
It has the same bad consequences as referencing uninitialized data --- core
dumps, wrong results, mysterious crashes.
よくあるメモリリークの原因はコード中の普通でない処理経路です。例えば、ある関数があるメモリブロックを確保し、何らかの計算を行って、再度ブロックを解放するとします。さて、関数の要求仕様を変更して、計算に対するテストを追加すると、エラー条件を検出し、関数の途中で処理を戻すようになるかもしれません。この途中での終了が起きるとき、確保されたメモリブロックは解放し忘れやすいのです。コードが後で追加された場合には特にそうです。このようなメモリリークが一旦紛れ込んでしまうと、長い間検出されないままになることがよくあります: エラーによる関数の終了は、全ての関数呼び出しのに対してほんのわずかな割合しか起きず、その一方でほとんどの近代的な計算機は相当量の仮想記憶を持っているため、メモリリークが明らかになるのは、長い間動作していたプロセスがリークを起こす関数を何度も使った場合に限られるからです。従って、この種のエラーを最小限にとどめるようなコーディング規約や戦略を設けて、不慮のメモリリークを避けることが重要なのです。
Python は malloc()
や free()
を非常によく利用するため、メモリリークの防止に加え、解放されたメモリの使用を防止する戦略が必要です。このために選ばれたのが参照カウント法 (reference counting) と呼ばれる手法です。参照カウント法の原理は簡単です: 全てのオブジェクトにはカウンタがあり、オブジェクトに対する参照がどこかに保存されたらカウンタをインクリメントし、オブジェクトに対する参照が削除されたらデクリメントします。カウンタがゼロになったら、オブジェクトへの最後の参照が削除されたことになり、オブジェクトは解放されます。
もう一つの戦略は自動ガベージコレクション (automatic garbage collection) と呼ばれています。 (参照カウント法はガベージコレクション戦略の一つとして挙げられることもあるので、二つを区別するために筆者は "自動 (automatic)" を使っています。) 自動ガベージコレクションの大きな利点は、ユーザが free()
を明示的によばなくてよいことにあります。 (速度やメモリの有効利用性も利点として主張されています --- が、これは確たる事実ではありません。) C における自動ガベージコレクションの欠点は、真に可搬性のあるガベージコレクタが存在しないということです。それに対し、参照カウント法は可搬性のある実装ができます (malloc()
や free()
を利用できるのが前提です --- C 標準はこれを保証しています)。いつの日か、十分可搬性のあるガベージコレクタが C で使えるようになるかもしれませんが、それまでは参照カウント法でやっていく以外にはないのです。
Python では、伝統的な参照カウント法の実装を行っている一方で、参照の循環を検出するために働く循環参照検出機構 (cycle detector) も提供しています。循環参照検出機構のおかげで、直接、間接にかかわらず循環参照の生成を気にせずにアプリケーションを構築できます; というのも、参照カウント法だけを使ったガベージコレクション実装にとって循環参照は弱点だからです。循環参照は、(間接参照の場合も含めて) 相互への参照が入ったオブジェクトから形成されるため、循環内のオブジェクトは各々非ゼロの参照カウントを持ちます。典型的な参照カウント法の実装では、たとえ循環参照を形成するオブジェクトに対して他に全く参照がないとしても、循環参照内のどのオブジェクトに属するメモリも再利用できません。
循環参照検出機構はそのようなガベージサイクル (前述したような循環参照オブジェクト) を検出して回収することができます。
gc
モジュールそのような検出機構の実行 (collect()
関数) を提供するとともに、設定のためのインタフェースおよび検出機構を実行時に無効にする方法も提供しています。
1.10.1. Python における参照カウント法¶
Python には、参照カウントのインクリメントやデクリメントを処理する二つのマクロ、 Py_INCREF(x)
と Py_DECREF(x)
があります。 Py_DECREF()
は、参照カウントがゼロに到達した際に、オブジェクトのメモリ解放も行います。柔軟性を持たせるために、 free()
を直接呼び出しません --- その代わりにオブジェクトの型オブジェクト (type object) を介します。このために (他の目的もありますが)、全てのオブジェクトには自身の型オブジェクトに対するポインタが入っています。
さて、まだ重大な疑問が残っています: いつ Py_INCREF(x)
や Py_DECREF(x)
を使えばよいのでしょうか? まず、いくつかの用語説明から始めさせてください。まず、オブジェクトは "占有 (own)" されることはありません; しかし、あるオブジェクトに対する参照の所有 own a reference はできます。オブジェクトの参照カウントは、そのオブジェクトが参照の所有を受けている回数と定義されています。参照の所有者は、参照が必要なくなった際に Py_DECREF()
を呼び出す役割を担います。参照の所有権は委譲 (transfer) できます。所有参照 (owned reference) の放棄には、渡す、保存する、 Py_DECREF()
を呼び出す、という三つの方法があります。所有参照を処理し忘れると、メモリリークを引き起こします。
オブジェクトに対する参照は、借用 (borrow) も可能です。 [2] 参照の借用者は、 Py_DECREF()
を呼んではなりません。借用者は、参照の所有者から借用した期間を超えて参照を保持し続けてはなりません。所有者が参照を放棄した後で借用参照を使うと、解放済みメモリを使用してしまう危険があるので、絶対に避けねばなりません。 [3]
参照の借用が参照の所有よりも優れている点は、コードがとりうるあらゆる処理経路で参照を廃棄しておくよう注意しなくて済むことです --- 別の言い方をすれば、借用参照の場合には、処理の途中で関数を終了してもメモリリークの危険を冒すことがない、ということです。逆に、所有よりも不利な点は、ごくまともに見えるコードが、実際には参照の借用元で放棄されてしまった後にその参照を使うかもしれないような微妙な状況があるということです。
Py_INCREF()
を呼び出すと、借用参照を所有参照に変更できます。この操作は参照の借用元の状態には影響しません --- Py_INCREF()
は新たな所有参照を生成し、参照の所有者が担うべき全ての責任を課します (つまり、新たな参照の所有者は、以前の所有者と同様、参照の放棄を適切に行わねばなりません)。
1.10.2. 所有権にまつわる規則¶
オブジェクトへの参照を関数の内外に渡す場合には、オブジェクトの所有権が参照と共に渡されるか否かが常に関数インターフェース仕様の一部となります。
オブジェクトへの参照を返すほとんどの関数は、参照とともに所有権も渡します。特に、 PyLong_FromLong()
や Py_BuildValue()
のように、新しいオブジェクトを生成する関数は全て所有権を相手に渡します。オブジェクトが実際には新たなオブジェクトでなくても、そのオブジェクトに対する新たな参照の所有権を得ます。例えば、 PyLong_FromLong()
はよく使う値をキャッシュしており、キャッシュされた値への参照を返すことがあります。
PyObject_GetAttrString()
のように、あるオブジェクトから別のオブジェクトを抽出するような関数もまた、参照とともに所有権を委譲します。こちらの方はやや理解しにくいかもしれません。というのはよく使われるルーチンのいくつかが例外となっているからです: PyTuple_GetItem()
、 PyList_GetItem()
、 PyDict_GetItem()
、および PyDict_GetItemString()
は全て、タプル、リスト、または辞書から借用参照を返します。
PyImport_AddModule()
は、実際にはオブジェクトを生成して返すことがあるにもかかわらず、借用参照を返します: これが可能なのは、生成されたオブジェクトに対する所有参照は sys.modules
に保持されるからです。
オブジェクトへの参照を別の関数に渡す場合、一般的には、関数側は呼び出し手から参照を借用します --- 参照を保存する必要があるなら、関数側は Py_INCREF()
を呼び出して独立した所有者になります。とはいえ、この規則には二つの重要な例外: PyTuple_SetItem()
と PyList_SetItem()
があります。これらの関数は、渡された引数要素に対して所有権を乗っ取り (take over) ます --- たとえ失敗してもです! (PyDict_SetItem()
とその仲間は所有権を乗っ取りません --- これらはいわば "普通の" 関数です。)
Python から C 関数が呼び出される際には、C 関数は呼び出し側から引数への参照を借用します。C 関数の呼び出し側はオブジェクトへの参照を所有しているので、借用参照の生存期間が保証されるのは関数が処理を返すまでです。このようにして借用参照を保存したり他に渡したりしたい場合にのみ、 Py_INCREF()
を使って所有参照にする必要があります。
Python から呼び出された C 関数が返す参照は所有参照でなければなりません --- 所有権は関数から呼び出し側へと委譲されます。
1.10.3. 薄氷¶
数少ない状況において、一見無害に見える借用参照の利用が問題をひきおこすことがあります。この問題はすべて、インタプリタが非明示的に呼び出され、インタプリタが参照の所有者に参照を放棄させてしまう状況と関係しています。
知っておくべきケースのうち最初の、そして最も重要なものは、リスト要素に対する参照を借りている際に起きる、関係ないオブジェクトに対する Py_DECREF()
の使用です。例えば:
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0); /* BUG! */
}
上の関数はまず、 list[0]
への参照を借用し、次に list[1]
を値 0
で置き換え、最後にさきほど借用した参照を出力しています。何も問題ないように見えますね? でもそうではないのです!
Let's follow the control flow into PyList_SetItem()
. The list owns
references to all its items, so when item 1 is replaced, it has to dispose of
the original item 1. Now let's suppose the original item 1 was an instance of a
user-defined class, and let's further suppose that the class defined a
__del__()
method. If this class instance has a reference count of 1,
disposing of it will call its __del__()
method.
Since it is written in Python, the __del__()
method can execute arbitrary
Python code. Could it perhaps do something to invalidate the reference to
item
in bug()
? You bet! Assuming that the list passed into
bug()
is accessible to the __del__()
method, it could execute a
statement to the effect of del list[0]
, and assuming this was the last
reference to that object, it would free the memory associated with it, thereby
invalidating item
.
問題の原因が分かれば、解決は簡単です。一時的に参照回数を増やせばよいのです。正しく動作するバージョンは以下のようになります:
void
no_bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_INCREF(item);
PyList_SetItem(list, 1, PyLong_FromLong(0L));
PyObject_Print(item, stdout, 0);
Py_DECREF(item);
}
This is a true story. An older version of Python contained variants of this bug
and someone spent a considerable amount of time in a C debugger to figure out
why his __del__()
methods would fail...
二つ目は、借用参照がスレッドに関係しているケースです。通常は、 Python インタプリタにおける複数のスレッドは、グローバルインタプリタロックがオブジェクト空間全体を保護しているため、互いに邪魔し合うことはありません。とはいえ、ロックは Py_BEGIN_ALLOW_THREADS
マクロで一時的に解除したり、 Py_END_ALLOW_THREADS
で再獲得したりできます。これらのマクロはブロックの起こる I/O 呼び出しの周囲によく置かれ、 I/O が完了するまでの間に他のスレッドがプロセッサを利用できるようにします。明らかに、以下の関数は上の例と似た問題をはらんでいます:
void
bug(PyObject *list)
{
PyObject *item = PyList_GetItem(list, 0);
Py_BEGIN_ALLOW_THREADS
...some blocking I/O call...
Py_END_ALLOW_THREADS
PyObject_Print(item, stdout, 0); /* BUG! */
}
1.10.4. NULL ポインタ¶
一般論として、オブジェクトへの参照を引数にとる関数はユーザが NULL
ポインタを渡すとは予想しておらず、渡そうとするとコアダンプになる (か、あとでコアダンプを引き起こす) ことでしょう。一方、オブジェクトへの参照を返すような関数は一般に、例外の発生を示す場合にのみ NULL
を返します。引数に対して NULL
テストを行わない理由は、関数はしばしば受け取ったオブジェクトを他の関数へと引き渡すからです --- 各々の関数が NULL
テストを行えば、冗長なテストが大量に行われ、コードはより低速に動くことになります。
従って、 NULL
のテストはオブジェクトの "発生源"、すなわち値が NULL
になるかもしれないポインタを受け取ったときだけにしましょう。 malloc()
や、例外を送出する可能性のある関数がその例です。
マクロ Py_INCREF()
および Py_DECREF()
は NULL
ポインタのチェックを行いません --- しかし、これらのマクロの変化形である Py_XINCREF()
および Py_XDECREF()
はチェックを行います。
特定のオブジェクト型について調べるマクロ (Pytype_Check()
) は NULL ポインタのチェックを行いません --- 繰り返しますが、様々な異なる型を想定してオブジェクトの型を調べる際には、こうしたマクロを続けて呼び出す必要があるので、個別に NULL
ポインタのチェックをすると冗長なテストになってしまうのです。型を調べるマクロには、 NULL
チェックを行う変化形はありません。
Python から C 関数を呼び出す機構は、C 関数に渡される引数リスト (例でいうところの args
) が決して NULL
にならないよう保証しています --- 実際には、常にタプル型になるよう保証しています。[4]
NULL
ポインタを Python ユーザレベルに "逃がし" てしまうと、深刻なエラーを引き起こします。
1.11. C++での拡張モジュール作成¶
C++でも拡張モジュールは作成できます。ただしいくつか制限があります。メインプログラム (Python インタプリタ) は C コンパイラでコンパイルされリンクされているので、グローバル変数や静的オブジェクトをコンストラクタで作成できません。メインプログラムが C++ コンパイラでリンクされているならこれは問題ではありません。 Python インタプリタから呼び出される関数 (特にモジュール初期化関数) は、 extern "C"
を使って宣言しなければなりません。また、Python ヘッダファイルを extern "C" {...}
に入れる必要はありません--- シンボル __cplusplus
(最近の C++ コンパイラは全てこのシンボルを定義しています) が定義されているときに extern "C" {...}
が行われるように、ヘッダファイル内にすでに書かれているからです。
1.12. 拡張モジュールに C API を提供する¶
多くの拡張モジュールは単に Python から使える新たな関数や型を提供するだけですが、時に拡張モジュール内のコードが他の拡張モジュールでも便利なことがあります。例えば、あるモジュールでは順序概念のないリストのように動作する "コレクション (collection)" クラスを実装しているかもしれません。ちょうどリストを生成したり操作したりできる C API を備えた標準の Python リスト型のように、この新たなコレクション型も他の拡張モジュールから直接操作できるようにするには一連の C 関数を持っていなければなりません。
一見するとこれは簡単なこと: 単に関数を (もちろん static
などとは宣言せずに) 書いて、適切なヘッダファイルを提供し、C API を書けばよいだけ、に思えます。そして実際のところ、全ての拡張モジュールが Python インタプリタに常に静的にリンクされている場合にはうまく動作します。ところがモジュールが共有ライブラリの場合には、一つのモジュールで定義されているシンボルが他のモジュールから不可視なことがあります。可視性の詳細はオペレーティングシステムによります; あるシステムは Python インタプリタと全ての拡張モジュール用に単一のグローバルな名前空間を用意しています (例えば Windows)。別のシステムはモジュールのリンク時に取り込まれるシンボルを明示的に指定する必要があります (AIX がその一例です)、また別のシステム (ほとんどの Unix) では、違った戦略を選択肢として提供しています。そして、たとえシンボルがグローバル変数として可視であっても、呼び出したい関数の入ったモジュールがまだロードされていないことだってあります!
従って、可搬性の点からシンボルの可視性には何ら仮定をしてはならないことになります。つまり拡張モジュール中の全てのシンボルは static
と宣言せねばなりません。例外はモジュールの初期化関数で、これは (モジュールのメソッドテーブルと初期化関数 で述べたように) 他の拡張モジュールとの間で名前が衝突するのを避けるためです。また、他の拡張モジュールからアクセスを 受けるべきではない シンボルは別のやり方で公開せねばなりません。
Python はある拡張モジュールの C レベルの情報 (ポインタ) を別のモジュールに渡すための特殊な機構: Capsule (カプセル)を提供しています。 Capsule はポインタ (void*) を記憶する Python のデータ型です。 Capsule は C API を介してのみ生成したりアクセスしたりできますが、他の Python オブジェクトと同じように受け渡しできます。とりわけ、Capsule は拡張モジュールの名前空間内にある名前に代入できます。他の拡張モジュールはこのモジュールを import でき、次に名前を取得し、最後にCapsule へのポインタを取得します。
拡張モジュールの C API を公開するために、様々な方法で Capsule が使われます。各関数を1つのオブジェクトに入れたり、全ての C API のポインタ配列を Capsule に入れることができます。そして、ポインタに対する保存や取得といった様々な作業は、コードを提供しているモジュールとクライアントモジュールとの間では異なる方法で分散できます。
どの方法を選ぶにしても、 Capsule の name を正しく設定することは重要です。 PyCapsule_New()
は name 引数 (const char*) を取ります。 NULL
を name に渡すことも許可されていますが、 name を設定することを強く推奨します。正しく名前を付けられた Capsule はある程度の実行時型安全性を持ちます。名前を付けられていない Capsule を他の Capsule と区別する現実的な方法はありません。
特に、 C API を公開するための Capsule には次のルールに従った名前を付けるべきです:
modulename.attributename
PyCapsule_Import()
という便利関数は、 Capsule の名前がこのルールに一致しているときにのみ、簡単に Capsule 経由で公開されている C API をロードすることができます。この挙動により、 C API のユーザーが、確実に正しい C API を格納している Capsule をロードできたことを確かめることができます。
以下の例では、名前を公開するモジュールの作者にほとんどの負荷が掛かりますが、よく使われるライブラリを作る際に適切なアプローチを実演します。このアプローチでは、全ての C API ポインタ (例中では一つだけですが!) を、 Capsule の値となる void ポインタの配列に保存します。拡張モジュールに対応するヘッダファイルは、モジュールの import と C API ポインタを取得するよう手配するマクロを提供します; クライアントモジュールは、C API にアクセスする前にこのマクロを呼ぶだけです。
The exporting module is a modification of the spam
module from section
簡単な例. The function spam.system()
does not call
the C library function system()
directly, but a function
PySpam_System()
, which would of course do something more complicated in
reality (such as adding "spam" to every command). This function
PySpam_System()
is also exported to other extension modules.
The function PySpam_System()
is a plain C function, declared
static
like everything else:
static int
PySpam_System(const char *command)
{
return system(command);
}
The function spam_system()
is modified in a trivial way:
static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts;
if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = PySpam_System(command);
return PyLong_FromLong(sts);
}
モジュールの先頭にある以下の行
#include <Python.h>
の直後に、以下の二行を必ず追加してください:
#define SPAM_MODULE
#include "spammodule.h"
#define
は、ファイル spammodule.h
をインクルードしているのが名前を公開する側のモジュールであって、クライアントモジュールではないことをヘッダファイルに教えるために使われます。最後に、モジュールの初期化関数は C API のポインタ配列を初期化するよう手配しなければなりません:
PyMODINIT_FUNC
PyInit_spam(void)
{
PyObject *m;
static void *PySpam_API[PySpam_API_pointers];
PyObject *c_api_object;
m = PyModule_Create(&spammodule);
if (m == NULL)
return NULL;
/* Initialize the C API pointer array */
PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;
/* Create a Capsule containing the API pointer array's address */
c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);
if (PyModule_Add(m, "_C_API", c_api_object) < 0) {
Py_DECREF(m);
return NULL;
}
return m;
}
Note that PySpam_API
is declared static
; otherwise the pointer
array would disappear when PyInit_spam()
terminates!
からくりの大部分はヘッダファイル spammodule.h
内にあり、以下のようになっています:
#ifndef Py_SPAMMODULE_H
#define Py_SPAMMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
/* Header file for spammodule */
/* C API functions */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)
/* Total number of C API pointers */
#define PySpam_API_pointers 1
#ifdef SPAM_MODULE
/* This section is used when compiling spammodule.c */
static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;
#else
/* This section is used in modules that use spammodule's API */
static void **PySpam_API;
#define PySpam_System \
(*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])
/* Return -1 on error, 0 on success.
* PyCapsule_Import will set an exception if there's an error.
*/
static int
import_spam(void)
{
PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);
return (PySpam_API != NULL) ? 0 : -1;
}
#endif
#ifdef __cplusplus
}
#endif
#endif /* !defined(Py_SPAMMODULE_H) */
All that a client module must do in order to have access to the function
PySpam_System()
is to call the function (or rather macro)
import_spam()
in its initialization function:
PyMODINIT_FUNC
PyInit_client(void)
{
PyObject *m;
m = PyModule_Create(&clientmodule);
if (m == NULL)
return NULL;
if (import_spam() < 0)
return NULL;
/* additional initialization can happen here */
return m;
}
このアプローチの主要な欠点は、 spammodule.h
がやや難解になるということです。とはいえ、各関数の基本的な構成は公開されるものと同じなので、書き方を一度だけ学べばすみます。
最後に、Capsule は、自身に保存されているポインタをメモリ確保したり解放したりする際に特に便利な、もう一つの機能を提供しているということに触れておかねばなりません。詳細は Python/C API リファレンスマニュアルの カプセル, および Capsule の実装部分 (Python ソースコード配布物中のファイル Include/pycapsule.h
および Objects/pycapsule.c
に述べられています。
脚注