Python - C による拡張プログラミング
前のページ次のページ
C、C++、Java などのコンパイル済み言語を使用して記述したコードは、別の Python スクリプトに統合またはインポートできます。このコードは「拡張機能」と見なされます。
Python 拡張モジュールは、通常の C ライブラリにすぎません。 Unix マシンでは、これらのライブラリは通常 .so で終わります。 (共有オブジェクト用)。 Windows マシンでは、通常、.dll が表示されます。 (動的にリンクされたライブラリの場合)
拡張機能を作成するための前提条件
拡張機能の作成を開始するには、Python ヘッダー ファイルが必要になります。
-
Unix マシンでは、これには通常、python2.5-dev などの開発者固有のパッケージをインストールする必要があります。
-
Windows ユーザーは、バイナリ Python インストーラーを使用するときに、これらのヘッダーをパッケージの一部として取得します。
さらに、C プログラミングを使用して Python 拡張機能を作成するために、C または C++ に関する十分な知識があることを前提としています。
Python 拡張機能の初見
Python 拡張モジュールを初めて見る場合は、コードを 4 つの部分にグループ化する必要があります −
-
ヘッダー ファイル Python.h .
-
モジュールからインターフェースとして公開する C 関数。
-
Python 開発者が参照する関数の名前を、拡張モジュール内の C 関数にマッピングするテーブル。
-
初期化関数。
ヘッダー ファイル Python.h
Python.h を含める必要があります これにより、モジュールをインタープリターにフックするために使用される内部 Python API にアクセスできます。
必要な他のヘッダーの前に、必ず Python.h を含めてください。 Python から呼び出したい関数を含むインクルードに従う必要があります。
C 関数
関数の C 実装の署名は、常に次の 3 つの形式のいずれかを取ります −
static PyObject *MyFunction( PyObject *self, PyObject *args ); static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw); static PyObject *MyFunctionWithNoArgs( PyObject *self );
上記の各宣言は、Python オブジェクトを返します。 ボイドなどというものはありません 関数が値を返さない場合は、Python の None に相当する C を返します。 価値。 Python ヘッダーは、これを行うマクロ Py_RETURN_NONE を定義します。
C 関数の名前は、拡張モジュールの外では決して見られないため、好きな名前にすることができます。それらは 静的 として定義されています 関数。
ここに示すように、C 関数は通常、Python モジュール名と関数名を組み合わせて命名されます −
static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; }
これは func という Python 関数です。 モジュール内 module . C 関数へのポインターを、通常はソース コードの次に来るモジュールのメソッド テーブルに配置します。
メソッド マッピング テーブル
このメソッド テーブルは、PyMethodDef 構造体の単純な配列です。その構造は次のようになります −
struct PyMethodDef { char *ml_name; PyCFunction ml_meth; int ml_flags; char *ml_doc; };
この構造体のメンバーの説明は次のとおりです-
-
ml_name − これは、Python プログラムで使用されるときに Python インタープリターが提示する関数の名前です。
-
ml_meth − これは、前のセクションで説明したシグネチャのいずれかを持つ関数へのアドレスでなければなりません。
-
ml_flags − これは、ml_meth が使用している 3 つの署名のどれかをインタープリターに伝えます。
-
通常、このフラグの値は METH_VARARGS です。
-
関数にキーワード引数を許可したい場合は、このフラグを METH_KEYWORDS でビットごとに OR することができます。
-
これは、引数を受け入れたくないことを示す METH_NOARGS の値を持つこともできます。
-
-
ml_doc − これは関数の docstring です。書きたくない場合は NULL にすることができます。
このテーブルは、適切なメンバーの NULL 値と 0 値で構成されるセンチネルで終了する必要があります。
例
上記で定義された関数の場合、次のメソッド マッピング テーブルがあります-
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } };
初期化関数
拡張モジュールの最後の部分は初期化関数です。この関数は、モジュールがロードされるときに Python インタープリターによって呼び出されます。関数の名前は initModule にする必要があります モジュール モジュールの名前です。
初期化関数は、ビルドするライブラリからエクスポートする必要があります。 Python ヘッダーは PyMODINIT_FUNC を定義して、コンパイルしている特定の環境で発生する適切な呪文を含めます。関数を定義するときにそれを使用するだけです。
あなたのC初期化関数は、通常、次の全体的な構造を持っています-
PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
ここに Py_InitModule3 の説明があります 関数 −
-
機能 − これはエクスポートされる関数です。
-
モジュール _メソッド − 上記で定義したマッピング テーブル名です。
-
docstring − これは、拡張機能に付けたいコメントです。
これをまとめると次のようになります −
#include <Python.h> static PyObject *module_func(PyObject *self, PyObject *args) { /* Do your stuff here. */ Py_RETURN_NONE; } static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { NULL, NULL, 0, NULL } }; PyMODINIT_FUNC initModule() { Py_InitModule3(func, module_methods, "docstring..."); }
例
上記のすべての概念を利用する簡単な例 −
#include <Python.h> static PyObject* helloworld(PyObject* self) { return Py_BuildValue("s", "Hello, Python extensions!!"); } static char helloworld_docs[] = "helloworld( ): Any message you want to put here!!\n"; static PyMethodDef helloworld_funcs[] = { {"helloworld", (PyCFunction)helloworld, METH_NOARGS, helloworld_docs}, {NULL} }; void inithelloworld(void) { Py_InitModule3("helloworld", helloworld_funcs, "Extension module example!"); }
ここで Py_BuildValue 関数は、Python 値を構築するために使用されます。上記のコードを hello.c ファイルに保存します。このモジュールをコンパイルしてインストールし、Python スクリプトから呼び出す方法を確認します。
拡張機能のビルドとインストール
distutils パッケージを使用すると、Python モジュール (純粋な Python モジュールと拡張モジュールの両方) を標準的な方法で簡単に配布できます。モジュールはソース形式で配布され、通常は setup.py と呼ばれるセットアップ スクリプトを介してビルドおよびインストールされます。
上記のモジュールでは、次の setup.py スクリプトを準備する必要があります −
from distutils.core import setup, Extension setup(name='helloworld', version='1.0', \ ext_modules=[Extension('helloworld', ['hello.c'])])
次に、次のコマンドを使用します。これにより、必要なすべてのコンパイルとリンクの手順が実行され、適切なコンパイラとリンカーのコマンドとフラグが使用され、結果の動的ライブラリが適切なディレクトリにコピーされます −
$ python setup.py install
Unix ベースのシステムでは、ほとんどの場合、このコマンドを root として実行して、site-packages ディレクトリへの書き込み権限を取得する必要があります。これは通常、Windows では問題になりません。
拡張機能のインポート
拡張機能をインストールしたら、次のように Python スクリプトでその拡張機能をインポートして呼び出すことができます −
#!/usr/bin/python import helloworld print helloworld.helloworld()
これにより、次の結果が生成されます-
Hello, Python extensions!!
関数パラメータを渡す
ほとんどの場合、引数を受け入れる関数を定義する必要があるため、C 関数には他のシグネチャの 1 つを使用できます。たとえば、いくつかのパラメーターを受け入れる次の関数は、次のように定義されます-
static PyObject *module_func(PyObject *self, PyObject *args) { /* Parse args and do something interesting here. */ Py_RETURN_NONE; }
新しい関数のエントリを含むメソッド テーブルは次のようになります −
static PyMethodDef module_methods[] = { { "func", (PyCFunction)module_func, METH_NOARGS, NULL }, { "func", module_func, METH_VARARGS, NULL }, { NULL, NULL, 0, NULL } };
API PyArg_ParseTuple を使用できます C 関数に渡された 1 つの PyObject ポインターから引数を抽出する関数
PyArg_ParseTuple の最初の引数は args 引数です。これは、解析するオブジェクトです . 2 番目の引数は、引数が期待どおりに表示されるように記述する書式文字列です。各引数は、次のようにフォーマット文字列の 1 つ以上の文字で表されます。
static PyObject *module_func(PyObject *self, PyObject *args) { int i; double d; char *s; if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) { return NULL; } /* Do something interesting here. */ Py_RETURN_NONE; }
モジュールの新しいバージョンをコンパイルしてインポートすると、任意のタイプの任意の数の引数で新しい関数を呼び出すことができます -
module.func(1, s="three", d=2.0) module.func(i=1, d=2.0, s="three") module.func(s="three", d=2.0, i=1)
おそらく、さらに多くのバリエーションを考え出すことができます.
PyArg_ParseTuple 機能
PyArg_ParseTuple の標準シグネチャは次のとおりです。 関数 −
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
この関数は、エラーの場合は 0 を返し、成功の場合は 0 以外の値を返します。 tuple は、C 関数の 2 番目の引数であった PyObject* です。ここでフォーマット 必須およびオプションの引数を記述する C 文字列です。
PyArg_ParseTuple のフォーマット コードのリストは次のとおりです。 関数 −
コード | C タイプ | 意味 |
---|---|---|
c | 文字 | 長さ 1 の Python 文字列は C 文字になります。 |
d | ダブル | Python float は C double になります。 |
f | フロート | Python float は C float になります。 |
私 | int | Python int は C int になります。 |
l | 長い | Python の int は C の long になります。 |
L | 長い長い | Python の int は C の long long になります |
O | PyObject* | Python 引数への非 NULL 借用参照を取得します。 |
s | char* | C char* にヌルが埋め込まれていない Python 文字列。 |
s# | char*+int | 任意の Python 文字列から C へのアドレスと長さ |
t# | char*+int | 読み取り専用のシングル セグメント バッファから C のアドレスと長さへ |
う | Py_UNICODE* | C への null が埋め込まれていない Python Unicode。 |
u# | Py_UNICODE*+int | 任意の Python Unicode C アドレスと長さ |
w# | char*+int | シングル セグメント バッファを C アドレスと長さに読み書きします。 |
z | char* | s と同様に、None も受け入れます (C char* を NULL に設定します)。 |
z# | char*+int | s# と同様に、None も受け入れます (C char* を NULL に設定します)。 |
(...) | ...に従って | Python シーケンスは、アイテムごとに 1 つの引数として扱われます。 |
| | 次の引数はオプションです。 | |
: | 書式の終わりにエラー メッセージの関数名が続きます。 | |
; | 形式の終わりに続いて、エラー メッセージ テキスト全体が続きます。 |
戻り値
Py_BuildValue PyArg_ParseTuple とよく似たフォーマット文字列を取ります します。作成する値のアドレスを渡す代わりに、実際の値を渡します。 add 関数を実装する方法を示す例を次に示します −
static PyObject *foo_add(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("i", a + b); }
これは、Python で実装した場合の外観です −
def add(a, b): return (a + b)
次のように、関数から 2 つの値を返すことができます。これは、Python のリストを使用してキャプチャされます。
static PyObject *foo_add_subtract(PyObject *self, PyObject *args) { int a; int b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("ii", a + b, a - b); }
これは、Python で実装した場合の外観です −
def add_subtract(a, b): return (a + b, a - b)
Py_BuildValue 機能
Py_BuildValue の標準シグネチャは次のとおりです。 関数 −
PyObject* Py_BuildValue(char* format,...)
ここでフォーマット ビルドする Python オブジェクトを記述する C 文字列です。 Py_BuildValue の次の引数 結果が構築される C 値です。 PyObject* 結果は新しい参照です。
次の表は、一般的に使用されるコード文字列を示しています。そのうちの 0 個以上が文字列形式に結合されています。
コード | C タイプ | 意味 |
---|---|---|
c | 文字 | C の文字は長さ 1 の Python 文字列になります。 |
d | ダブル | C の double は Python の float になります。 |
f | フロート | C float は Python float になります。 |
私 | int | C の int は Python の int になります。 |
l | 長い | C long は Python int になります。 |
N | PyObject* | Python オブジェクトを渡し、参照を盗みます。 |
O | PyObject* | Python オブジェクトを渡し、通常どおり INCREF します。 |
O& | convert+void* | 任意の変換 |
s | char* | C 0-terminated char* から Python 文字列へ、または NULL から None へ。 |
s# | char*+int | C char* および長さから Python 文字列へ、または NULL から None へ。 |
う | Py_UNICODE* | C ワイドの null で終わる文字列から Python Unicode へ、または NULL から None へ。 |
u# | Py_UNICODE*+int | C ワイドの文字列と長さから Python Unicode へ、または NULL から None へ。 |
w# | char*+int | シングル セグメント バッファを C アドレスと長さに読み書きします。 |
z | char* | s と同様に、None も受け入れます (C char* を NULL に設定します)。 |
z# | char*+int | s# と同様に、None も受け入れます (C char* を NULL に設定します)。 |
(...) | ...に従って | C 値から Python タプルを構築します。 |
[...] | ...に従って | C 値から Python リストを作成します。 |
{...} | ...に従って | キーと値を交互に使用して、C 値から Python 辞書を作成します。 |
コード {...} は、偶数個の C 値、交互にキーと値から辞書を構築します。たとえば、Py_BuildValue("{issi}",23,"zig","zag",42) は、Python の {23:'zig','zag':42} のような辞書を返します。
Python