.. PythonのC言語拡張モジュール作成ガイド documentation master file, created by sphinx-quickstart on Thu Jun 17 02:47:40 2010. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. :tocdepth: 2 ===================================== PythonのC言語拡張モジュール作成ガイド ===================================== | Version: |version| | Last Update: |today| .. contents:: :depth: 2 Python API =============== * Python/C APIチュートリアル : http://docs.python.org/extending/index.html * Python/C APIリファレンス : http://docs.python.org/c-api/index.html Python APIはPythonインストール標準のC言語APIを使用し、拡張モジュールを作成します。 使用ファイル ------------ PythonAPIを使用したCソースを準備します。 .. code-block:: c #include static PyObject * fact(PyObject *self, PyObject *args) { int n; int i; int ret=1; if (!PyArg_ParseTuple(args, "i", &n)) return NULL; for (i=n; i>0; i--) ret *= i; return Py_BuildValue("i", ret); } static PyObject * hello(PyObject *self) { printf("Hello World!!\n"); Py_RETURN_NONE; } static char ext_doc[] = "C extention module example\n"; static PyMethodDef methods[] = { {"hello", (PyCFunction)hello, METH_NOARGS, "print hello world.\n"}, {"fact", fact, METH_VARARGS, "return factorial.\n"}, {NULL, NULL, 0, NULL} }; void initext(void) { Py_InitModule3("ext", methods, ext_doc); } 作成方法 -------- .. code-block:: bash $ gcc -Wall -fPIC -c ext.c -I/usr/include/python2.6 $ gcc -shared -o ext.so ext.o $ ls ext.c ext.o ext.so .. code-block:: python >>> import ext >>> ext.hello() Hello World!! >>> ext.fact(5) 120 >>> ext.fact(10) 3628800 ctypes ====== * ctypesプロジェクトページ : http://python.net/crew/theller/ctypes/ * Pythonドキュメント - ctypes : http://docs.python.org/library/ctypes.html PythonAPIに最適化されていない生のCソースで作成した共有ライブラリ内の関数を モジュールアクセスで呼び出すことができます。 Python2.5からは標準ライブラリとして使用することができます。 使用ファイル ------------ 以下のようなCソースを準備するだけです。 .. code-block:: c #include void hello(void) { printf("Hello World!!\n"); } int fact(int n) { int i; int ret=1; for (i=n; i>0; i--) ret *= i; return ret; } 作成方法 -------- GCCを使っていつもどおりの共有ライブラリを作成します。 .. code-block:: c gcc -Wall -fPIC -c ext.c -I/usr/include/python2.6 gcc -shared -o ext.so ext.o 使用方法 -------- :mod:`ctypes` を使ってモジュールにアクセスしてみます。 .. code-block:: python >>> import ctypes >>> ext = ctypes.CDLL("./ext.so") >>> ext.hello() Hello World!! >>> ext.fact(5) 120 >>> ext.fact(10) 3628800 SWIG ==== プロジェクトページ : SWIG_ .. _SWIG: http://www.swig.org/ 別途インターフェースファイル(\*.i)を用意することで、 Cのスタイルを保ったコードで拡張モジュールを記述できます。 使ってみた感想としては、ラッピング関数を定義したファイルが作成され ファイル構成がやや煩雑になりますが、PythonAPIをいちいち調べたりしなくてもよいので、 より規模の大きなモジュールを作成する際などは、生産性の面で有利かなと感じました。 SWIGがインストールされている必要があります。 使用ファイル ------------ Cソース( :file:`ext.c` )は以下になります。 .. code-block:: c #include void hello(void) { printf("Hello World!!\n"); } int fact(int n) { int i; int ret=1; for (i=n; i>0; i--) ret *= i; return ret; } インターフェースファイル( :file:`ext.i` )は以下のような内容になります。 .. code-block:: c %module ext %{ extern int fact(int n); extern void hello(void); %} extern int fact(int n); extern void hello(void); 作成方法 -------- *swig* コマンドで後ほど作成する共有ライブラリ( :file:`_ext.so` )にアクセスする Pythonのラッパースクリプト( :file:`ext.py` )と ext.cをラッピングするCソース( :file:`ext_wrap.c`)が作成されます。 .. code-block:: bash $ swig -python ext.i $ ls ext.py ext_wrap.c $ gcc -Wall -fPIC -c ext.c ext_wrap.c -I/usr/include/python2.6 $ gcc -shared -o _ext.so ext.o ext_wrap.o Pyrex ===== プロジェクトページ : `Pyrex`_ .. _`Pyrex`: http://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/ Python拡張モジュール作成用のPythonライクな構文を持つ言語と、 それをC言語に変換するツールセット(pyrexc)を含む処理系です。 個人的には若干構文が気に入らない感じです。Pyrex構文を覚えないとだめなのが。。。 .. code-block:: python def hello(): print "Hello World!!" def fact(int n): cdef int i cdef int ret ret = 1 i = n while i > 0: ret *= i i -= 1 return ret .. code-block:: python $ pyrexc ext.pyx $ gcc -Wall -fPIC -c ext.c -I/usr/include/python2.6 $ gcc -shared -o ext.so ext.o *pyrexc* での変換がどこまで対応できるのかが気になります。 Cython ====== プロジェクトページ : `Cython`_ Pyrexの派生であり、Pythonと上位互換があります。 よってPyrexよりもよりPythonらしく記述できます。 .. _`Cython`: http://www.cython.org/ .. code-block:: python def hello(): print "Hello World!!" def fact(n): ret = 1 i = n while i > 0: ret *= i i -= 1 return ret .. code-block:: python from distutils.core import setup from distutils.extension import Extension from Cython.Distutils import build_ext ext_modules = [Extension("ext", ["ext.pyx"])] setup( name = 'C extention module example', cmdclass = {'build_ext': build_ext}, ext_modules = ext_modules ) .. code-block:: bash $ python setup.py build_ext --inplace $ ls build ext.c ext.so Pyrexと同じく大規模なモジュールでうまく作成できるかどうかが 気になるところです。 CFFI ==== * プロジェクトページ : `CFFI`_ * PYPI : `cffi on PyPI`_ まず、ある言語から別の言語で作成されたライブラリを呼び出す仕組みとして FFI(Foreign Function Interface)があります。 CFFIはFFIの仕組みを利用して、PythonからCで作成されたライブラリを呼び出す仕組みです。 .. _`CFFI`: http://cffi.readthedocs.org/ .. _`cffi on PyPI`: https://pypi.python.org/pypi/cffi 共有ライブラリにアクセスする ---------------------------- Cで作成した共有ライブラリ( :file:`ext.so` )を ``cffi.FFI.dlopen`` 経由で使用するPythonスクリプトは以下です。 .. code-block:: python from cffi import FFI ffi = FFI() ffi.cdef(""" void hello(void); int fact(int n); """) lib = ffi.dlopen("./ext.so") lib.hello() print lib.fact(5), lib.fact(10) 直接Cのコードを記述する ----------------------- ``cffi.FFI.verify`` を使用することでCのコードを直接記述し、呼び出すことができます。 以下がその例です。 .. code-block:: python from cffi import FFI ffi = FFI() ffi.cdef(""" void hello(void); int fact(int n); """) _C = r""" #include void hello(void) { printf("Hello World!!\n"); } int fact(int n) { int i; int ret=1; for (i=n; i>0; i--) ret *= i; return ret; } """ lib = ffi.verify(_C) lib.hello() print lib.fact(5), lib.fact(10) 変数や構造体を使う ------------------ ``cffi.FFI.cdef`` に直接構造体(struct)定義を記述し、 ``cffi.FFI.new`` 経由で変数を使用してみます。 .. code-block:: python from cffi import FFI ffi = FFI() ffi.cdef(r""" typedef struct { int age; double weight; double height; } Person; void use_struct(Person *p); void use_char(char *name); """) _C = r""" #include typedef struct { int age; double weight; double height; } Person; void use_struct(Person *p) { printf("age : %d\n", p->age); printf("weight: %.2lf\n", p->weight); printf("height: %.2lf\n", p->height); } void use_char(char *name) { printf("Full Name: %s\n", name); } """ lib = ffi.verify(_C) mike = ffi.new("Person[]", [(25, 181.2, 90.5)]) lib.use_struct(mike) susan_fullname = "Suzan Zizi" lib.use_char(susan_fullname) 参考サイト ========== * http://d.hatena.ne.jp/niitsuma/20080209/1203184397 swig boost.python ctypes 等のまとめ