Python3-python-further-extensions

提供:Dev Guides
移動先:案内検索

Python 3-Cを使用した拡張プログラミング

C、C++、Javaなどのコンパイル済み言語を使用して記述するコードは、別のPythonスクリプトに統合またはインポートできます。 このコードは「拡張機能」と見なされます。

Python拡張モジュールは、通常のCライブラリにすぎません。 Unixマシンでは、これらのライブラリは通常 .so (共有オブジェクト用)で終わります。 Windowsマシンでは、通常、*。dll *(動的にリンクされたライブラリ用)が表示されます。

拡張機能を記述するための前提条件

拡張機能の作成を開始するには、Pythonヘッダーファイルが必要になります。

  • Unixマシンでは、通常、python2.5-devなどの開発者固有のパッケージをインストールする必要があります。
  • Windowsユーザーは、バイナリPythonインストーラーを使用すると、パッケージの一部としてこれらのヘッダーを取得します。

さらに、CまたはC++について十分な知識があることを前提としています。 Cプログラミングを使用してPython拡張機能を記述します。

Python拡張機能の最初の検討

Python拡張モジュールを初めて見るには、コードを4つの部分にグループ化する必要があります-

  • ヘッダーファイル_Python.h_。
  • モジュールからインターフェイスとして公開するC関数。
  • Python開発者が関数を拡張モジュール内のC関数と見なすときに関数の名前をマッピングするテーブル。 *初期化関数。

ヘッダーファイルPython.h

Cソースファイルに_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には、Cにあるような_void_関数などはありません。 関数が値を返さないようにするには、Pythonの None 値に相当するCを返します。 Pythonヘッダーは、これを行うマクロPy_RETURN_NONEを定義します。

C関数の名前は、拡張モジュールの外では決して見られないため、好きな名前にできます。 それらは_static_関数として定義されています。

あなたのC関数は通常、ここに示すように、Pythonモジュールと関数名を組み合わせて名前が付けられます-

static PyObject *module_func(PyObject *self, PyObject *args) {
  /*Do your stuff here.*/
   Py_RETURN_NONE;
}

これは、モジュール_module_内の_func_と呼ばれるPython関数です。 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とビット単位で論理和することができます。
  • また、引数を受け入れたくないことを示す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 である必要があります。ここで、_Module_はモジュールの名前です。

初期化関数は、ビルドするライブラリからエクスポートする必要があります。 PythonヘッダーはPyMODINIT_FUNCを定義して、コンパイルする特定の環境で発生する適切な呪文を含めます。 しなければならないことは、関数を定義するときにそれを使用することだけです。

あなたのC初期化関数は一般的に次の全体的な構造を持っています-

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

ここに Py_InitModule3 関数の説明があります-

  • func -これはエクスポートされる関数です。
  • module_methods -これは上で定義されたマッピングテーブル名です。
  • 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 asと呼ばれるセットアップスクリプトを介してビルドおよびインストールされます。

上記のモジュールでは、次の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/python3
import helloworld

print helloworld.helloworld()

出力

これは、次の結果を生成します-

Hello, Python extensions!!

関数パラメーターを渡す

ほとんどの場合、引数を受け入れる関数を定義する必要があるため、C関数に他のシグネチャのいずれかを使用できます。 たとえば、いくつかのパラメータを受け入れる次の関数は、このように定義されます-

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引数です。 これは、_parsing_になるオブジェクトです。 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* です。 ここで、_format_は、必須およびオプションの引数を説明するC文字列です。

*PyArg_ParseTuple* 関数のフォーマットコードのリストは次のとおりです-
Code C type Meaning
c char A Python string of length 1 becomes a C char.
d double A Python float becomes a C double.
f float A Python float becomes a C float.
i int A Python int becomes a C int.
l long A Python int becomes a C long.
L long long A Python int becomes a C long long
O PyObject* Gets non-NULL borrowed reference to Python argument.
s char* Python string without embedded nulls to C char*.
s# char*PLUSint Any Python string to C address and length.
t# char*PLUSint Read-only single-segment buffer to C address and length.
u Py_UNICODE* Python Unicode without embedded nulls to C.
u# Py_UNICODE*PLUSint Any Python Unicode C address and length.
w# char*+int Read/write single-segment buffer to C address and length.
z char* Like s, also accepts None (sets C char* to NULL).
z# char*+int Like s#, also accepts None (sets C char *to NULL).
(…​) as per …​ A Python sequence is treated as one argument per item.
The following arguments are optional. :
Format end, followed by function name for error messages. ;

戻り値

_Py_BuildValue_は、_PyArg_ParseTuple_と同様にフォーマット文字列を受け取ります。 構築する値のアドレスを渡す代わりに、実際の値を渡します。 追加機能を実装する方法を示す例があります-

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,...)

ここで、_format_は、ビルドするPythonオブジェクトを記述するC文字列です。 _Py_BuildValue_の以下の引数は、結果が構築されるC値です。 _PyObject* _の結果は新しい参照です。

次の表は、一般に使用されるコード文字列のリストです。コード文字列のうち、0個以上が文字列形式に結合されています。

Code C type Meaning
c char A C char becomes a Python string of length 1.
d double A C double becomes a Python float.
f float A C float becomes a Python float.
i int A C int becomes a Python int.
l long A C long becomes a Python int.
N PyObject* Passes a Python object and steals a reference.
O PyObject* Passes a Python object and INCREFs it as normal.
O& convertPLUSvoid* Arbitrary conversion
s char* C 0-terminated char* to Python string, or NULL to None.
s# char*PLUSint C char* and length to Python string, or NULL to None.
u Py_UNICODE* C-wide, null-terminated string to Python Unicode, or NULL to None.
u# Py_UNICODE*PLUSint C-wide string and length to Python Unicode, or NULL to None.
w# char*PLUSint Read/write single-segment buffer to C address and length.
z char* Like s, also accepts None (sets C char* to NULL).
z# char*PLUSint Like s#, also accepts None (sets C char* to NULL).
(…​) as per …​ Builds Python tuple from C values.
[…​] as per …​ Builds Python list from C values.
\{…​} as per …​ Builds Python dictionary from C values, alternating keys and values.

コード\ {…​}は、キーと値を交互に使用して、偶数個のC値から辞書を作成します。 たとえば、Py_BuildValue( "\ {issi}"、23、 "zig"、 "zag"、42)は、Pythonの\ {23: 'zig'、 'zag':42}のような辞書を返します。