Contents
Python APIはPythonインストール標準のC言語APIを使用し、拡張モジュールを作成します。
PythonAPIを使用したCソースを準備します。
#include <Python.h>
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);
}
$ 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
>>> import ext
>>> ext.hello()
Hello World!!
>>> ext.fact(5)
120
>>> ext.fact(10)
3628800
PythonAPIに最適化されていない生のCソースで作成した共有ライブラリ内の関数を モジュールアクセスで呼び出すことができます。 Python2.5からは標準ライブラリとして使用することができます。
以下のようなCソースを準備するだけです。
#include <stdio.h>
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を使っていつもどおりの共有ライブラリを作成します。
gcc -Wall -fPIC -c ext.c -I/usr/include/python2.6
gcc -shared -o ext.so ext.o
ctypes を使ってモジュールにアクセスしてみます。
>>> import ctypes
>>> ext = ctypes.CDLL("./ext.so")
>>> ext.hello()
Hello World!!
>>> ext.fact(5)
120
>>> ext.fact(10)
3628800
プロジェクトページ : SWIG
別途インターフェースファイル(*.i)を用意することで、 Cのスタイルを保ったコードで拡張モジュールを記述できます。 使ってみた感想としては、ラッピング関数を定義したファイルが作成され ファイル構成がやや煩雑になりますが、PythonAPIをいちいち調べたりしなくてもよいので、 より規模の大きなモジュールを作成する際などは、生産性の面で有利かなと感じました。 SWIGがインストールされている必要があります。
Cソース( ext.c )は以下になります。
#include <stdio.h>
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;
}
インターフェースファイル( ext.i )は以下のような内容になります。
%module ext
%{
extern int fact(int n);
extern void hello(void);
%}
extern int fact(int n);
extern void hello(void);
swig コマンドで後ほど作成する共有ライブラリ( _ext.so )にアクセスする Pythonのラッパースクリプト( ext.py )と ext.cをラッピングするCソース( ext_wrap.c)が作成されます。
$ 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
Python拡張モジュール作成用のPythonライクな構文を持つ言語と、 それをC言語に変換するツールセット(pyrexc)を含む処理系です。 個人的には若干構文が気に入らない感じです。Pyrex構文を覚えないとだめなのが。。。
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
$ pyrexc ext.pyx
$ gcc -Wall -fPIC -c ext.c -I/usr/include/python2.6
$ gcc -shared -o ext.so ext.o
pyrexc での変換がどこまで対応できるのかが気になります。
プロジェクトページ : Cython
Pyrexの派生であり、Pythonと上位互換があります。 よってPyrexよりもよりPythonらしく記述できます。
def hello():
print "Hello World!!"
def fact(n):
ret = 1
i = n
while i > 0:
ret *= i
i -= 1
return ret
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
)
$ python setup.py build_ext --inplace
$ ls
build ext.c ext.so
Pyrexと同じく大規模なモジュールでうまく作成できるかどうかが 気になるところです。
まず、ある言語から別の言語で作成されたライブラリを呼び出す仕組みとして FFI(Foreign Function Interface)があります。
CFFIはFFIの仕組みを利用して、PythonからCで作成されたライブラリを呼び出す仕組みです。
Cで作成した共有ライブラリ( ext.so )を cffi.FFI.dlopen 経由で使用する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)
cffi.FFI.verify を使用することでCのコードを直接記述し、呼び出すことができます。 以下がその例です。
from cffi import FFI
ffi = FFI()
ffi.cdef("""
void hello(void);
int fact(int n);
""")
_C = r"""
#include <stdio.h>
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 経由で変数を使用してみます。
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 <stdio.h>
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)