【Pythonノート】Pythonサードパーティ拡張パッケージをC言語で実現する方法


PythonがC/C++をサポートするサードパーティ製拡張パッケージは、パフォーマンスが要求される場合に特に重要です.
ここでは、Python拡張パッケージをカスタマイズするための基本手順を例として説明します.
1.拡張パッケージソース実装
Python公式サイトチュートリアルExtending Python with C or C++の手順に従って、拡張モジュールのソースファイルは以下のように実現されます.
#include <Python.h>

// forward declaration
void initpyext(void);

// self-defined error obj
static PyObject * ExtError;

int main(int argc, char * argv[])
{
    // Pass argv[0] to the Python interpreter
    Py_SetProgramName(argv[0]);
    // Initialize the Python interpreter. Required.
    Py_Initialize();
    
    initpyext();

    Py_Exit(0);
    // not reached, warning will be printed when build the extension
}

static PyObject * py_ext_test_func(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(ExtError, "System command failed");
        return NULL;
    }

    return PyLong_FromLong(sts);
}

static PyMethodDef PyextMethods[] = {
    {"ext_cmd", py_ext_test_func, METH_VARARGS, "Execute a shell command." },
    {NULL, NULL, 0, NULL} 
};

PyMODINIT_FUNC initpyext(void)
{
    PyObject * m;
    m = Py_InitModule("pyext", PyextMethods);
    if(NULL == m) {
        return;
    }

    ExtError = PyErr_NewException("pyext.error", NULL, NULL);
    Py_INCREF(ExtError);
    PyModule_AddObject(m, "error", ExtError);
}

上のコードは典型的なPythonに対するc拡張ソースレイアウトを示している:1.main関数を拡張モジュールのエントリ2として定義する.main関数はinitpyext()を呼び出し、initpyext関数名はinitname形式でなければなりません.ここでnameは拡張モジュール名(拡張モジュール名はPy_InitModuleのパラメータで指定)3.initpyext関数では、Method listがstatic配列PyextMethodsで定義された4.PyextMethods配列で、拡張モジュールでサポートされているmethodsのnameとそれに対応する関数ポインタ5を登録するPyextMethods配列に登録されているメソッド名をPy_InitModuleが初期化するように呼び出す.od具体的な動作を定義し実現する
2.拡張ファイルのコンパイル/リンク
公式サイトのドキュメントBuilding C and C++Extensions with distutilsに従って、pythonサードパーティのpackageディレクトリに拡張をコンパイルしてインストールできます.
$> python setup.py build
running build
running build_ext
building 'pyext' extension
gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/slvher/tools/python-2.7.5/include/python2.7 -c pyext.c -o build/temp.linux-x86_64-2.7/pyext.o
pyext.c: In function `main':
pyext.c:28: warning: control reaches end of non-void function
gcc -pthread -shared build/temp.linux-x86_64-2.7/pyext.o -o build/lib.linux-x86_64-2.7/pyext.so

注意:ここではpythonでsetupを直接呼び出します.pyは、PATHにpythonのデフォルト環境変数が設定されているため、現在のシステムのデフォルトのpython解釈器が拡張解釈器をコンパイルするために使用されていない場合は、pythonの絶対パスで呼び出す必要があります.
3.拡張パッケージのインストール
$> python setup.py install
running install
running build
running build_ext
running install_lib
copying build/lib.linux-x86_64-2.7/pyext.so -> /home/slvher/tools/python-2.7.5/lib/python2.7/site-packages
running install_egg_info
Writing /home/slvher/tools/python-2.7.5/lib/python2.7/site-packages/TestPyExtPackage-1.0-py2.7.egg-info

4.使用可能なインストールが成功したことを確認したら、python解釈器に入り、import拡張モジュールの後、メソッド名を呼び出すことができます.
>>> import pyext
>>> pyext.ext_cmd("echo hello world")
hello world
0L
>>> 

【参考資料】1.Extending Python with C or C++
2. Building C and C++ Extensions with distutils
====================== EOF ==================