More Related Content Similar to Wrapping a C++ library with Cython Similar to Wrapping a C++ library with Cython (20) Wrapping a C++ library with Cython1. Wrapping a C++ library with Cython
Tokyo.SciPy #5
2013-01-26
1 / 39
2. 概要
Cython を用いた C/C++ライブラリの Python ラッパー作成
Python/C API
の初歩について説明する.
2 / 39
3. Outline
1. Introduction
2. Cython
3. Python/C API
4. Summary
3 / 39
5. 動機
Python と C/C++の両方の長所を活かしたい
C/C++ 実行速度の速さ
データ構造, 数値計算ライブラリが揃っている
→ 計算量が支配的な箇所に使う
Python 読み書きしやすさ, 柔軟性, 拡張性, ポータビリティ
利便性の高いライブラリが揃っている
→ その他のすべての箇所に使う
データ収集, 前処理
コマンドライン, GUI アプリ, ウェブアプリ
設定ファイル, ジョブ管理, ログ管理
テスト, 可視化, ドキュメンテーション, · · ·
⇒ Python から C/C++を呼び出せればよい
5 / 39
7. Python/C API
Python/C API を用いて Python の拡張モジュールを作成することに
より以下が可能となる.
新しいオブジェクトの追加
C/C++の呼び出し
http://docs.python.jp/2/c-api/index.html
http://docs.python.jp/2/extending/extending.html
7 / 39
8. say.c
#include <Python.h>
static PyObject* say_hello(PyObject* self, PyObject* args) {
const char* name;
if (!PyArg_ParseTuple(args, "s", &name))
return NULL;
printf("Hello %s!n", name);
Py_RETURN_NONE;
}
...
setup.py
setup(name="say", ext_modules=[
Extension("say", ["say.c"])
])
8 / 39
9. $ python setup.py build_ext --inplace
$ python
>>> import say
Python の内部構造についての知識が必要
メモリ管理, 例外処理などが面倒
コード量が多い
⇒ 拡張モジュールを作成するためのツールを使う
9 / 39
12. Cython
Python の拡張モジュールを作成するための言語
Python + 型宣言を基本とした言語仕様
CPython 2.4-3.x, Windows/Mac/Linux
Apache License
lxml, Numpy, Scipy, Sage, mpi4py, petsc4py, · · ·
http://cython.org/
12 / 39
13. 拡張モジュールの作成方法
1. pyx ファイルを作成する
2. Cython を用いて pyx ファイルを c/cpp ファイルに変換する
3. c/cpp ファイルをコンパイルする
生成された so ファイルは Python から直接インポートできる.
setup(ext_modules=cythonize("foo.pyx"))
$ python setup.py build_ext --inplace
$ python
>>> import foo
13 / 39
14. Cython では Python のソースコードが (ほぼ) そのまま使える
さらに以下のような言語仕様が加えられている.
型宣言
C/C++の読み込み
条件付きコンパイル
コンパイラディレクティブ
etc.
14 / 39
15. 型宣言
cdef int i, j[10]
cdef float f, *g
cdef struct Rectangle:
float width
float height
cdef enum State:
open = 1
closed = 2
cdef object pyobj
ctypedef unsigned long uint64_t
from libc.stdint cimport int64_t
15 / 39
16. 型変換
基本的な数値型と文字列型については, Python オブジェクトと C
変数が自動変換される.
cdef bytes py_byte_string
cdef unicode py_unicode_string
cdef char* c_string
py_byte_string = <bytes> c_string
py_byte_string = c_string
py_byte_string = c_string[:length]
c_string = py_byte_string
py_unicode_string = py_byte_string.decode("utf-8")
py_byte_string = py_unicde_string.encode("utf-8")
16 / 39
17. 関数定義
def により定義
引数, 返り値ともに Python オブジェクト
Python から呼び出せる
cdef により定義
引数, 返り値ともに C 変数 (Python オブジェクトも含む)
Python から呼び出せない
def integrate(double a, double b, int N):
# 引数, 返り値は自動的に型変換される
cdef int i
cdef double s, dx
s = 0; dx = (b - a) / N
for i in range(N):
s += f(a + i * dx)
return s * dx
cdef float f(double x) except *:
return 1 / x
17 / 39
18. 拡張型
cdef class Interval:
cdef public float x0, x1
def __init__(self, x0, x1):
self.x0 = x0; self.x1 = x1
@property
def length(self):
return self.x1 - self.x0
def widen(Interval i not None, r):
i.x0 *= r; i.x1 *= r
ビルトイン型, 拡張型を継承できる. 多重継承はできない.
アトリビュートには public, readonly を指定できる
拡張型の値は None を取りうる
拡張型の引数には not None を指定できる
<MyClass?> は型チェック付きキャスト
18 / 39
19. 拡張型の初期化
cinit C レベルの初期化を行う.
必ず一度だけ呼び出される.
この時点では Python オブジェクトとして不完全.
init cinit 以外の初期化を行う.
複数回呼ばれる/1 回も呼ばれない場合もある.
dealloc C レベルの破棄処理.
この時点では Python オブジェクトとして不完全.
基底型の cinit が先に呼び出される.
コンストラクタに渡した引数は cinit , init の両方に渡さ
れる.
19 / 39
20. C 言語とのシンタックスの違い
const は使えない
ヌルポインタは NULL により表す
p->a の代わりに p.a を使う
&x の代わりに cython.address(x) を使う
*p の代わりに p[0] or cython.operator.dereference(p)
を使う
20 / 39
21. C の読み込み
Cython は C のヘッダファイルを読まないため, 以下のような宣言
が必要となる.
cdef extern from "math.h":
double sin(double)
double M_PI
def py_sin(d):
# Python から呼び出し可能
return sin(M_PI / 180.0 * d)
21 / 39
22. C++の読み込み
名前空間
クラス
テンプレート
演算子オーバーロード
ポリモーフィズム
などに対応している.
C++の例外は対応する Python の例外に翻訳される.
22 / 39
23. STL コンテナ
STL コンテナは libcpp 以下から cimport するだけで使える
対応する Python 組み込み型があれば自動変換される
from libcpp.string cimport string
cdef string cpp_string
cdef bytes py_byte_string
cpp_string = <string> py_byte_string
cpp_string = py_byte_string
py_byte_string = cpp_string
23 / 39
24. C++クラスの宣言
pair.pyx
cdef extern from "<utility>" namespace "std":
cdef cppclass pair[T, U]:
T first
U second
pair() nogil except +
pair(pair&) nogil except +
pair(T&, U&) nogil except +
bint operator==(pair&, pair&) nogil
bint operator!=(pair&, pair&) nogil
...
cdef pair[int, char*] *p = new pair[int, char*](1, "One")
setup.py
setup(ext_modules=cythonize("pair.pyx", language="c++"))
24 / 39
25. Python ラッパークラス
cppclass を Python から呼び出すにはラッパークラスが必要.
cdef class PyPair:
cdef pair[int, char*] *thisptr
def __cinit__(self, *args, **kw):
self.thisptr = new pair[int, char*]()
def __init__(self, int i, char* s):
self.thisptr.first = i
self.thisptr.second = s
def __dealloc__(self):
del self.thisptr
@property
def first(self):
return self.thisptr.first
@property
def second(self):
return self.thisptr.second
25 / 39
26. デバッガ
$ python-dbg setup.py build_ext --pyrex-gdb --inplace
$ cygdb
(gdb)
使い方は GDB とほぼ同じ.
ブレークポイントの設定
スタックのインスペクション
ステップ実行
etc.
26 / 39
27. typedness のアノテーション
Cython および C/C++コードを typedness により色分けした HTML
ファイルを生成する.
$ cython foo.pyx -a
http://docs.cython.org/src/quickstart/cythonize.html
27 / 39
29. その他の機能
Numpy との連携
Sage Notebook との連携
コンパイラディレクティブ
条件付きコンパイル
融合型
型つきメモリビュー
並列化
GIL 制御
etc.
詳しくは http://docs.cython.org/
29 / 39
32. すべてのデータはオブジェクト
#define PyObject_HEAD
_PyObject_HEAD_EXTRA /* デバグ用*/
Py_ssize_t ob_refcnt; /* 参照カウンタ */
struct _typeobject *ob_type; /* 型オブジェクト */
typedef struct _object {
PyObject_HEAD
} PyObject;
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
PyInt FromLong などで Python オブジェクトを構築
PyInt Check などで型チェ ック
PyInt AsLong などで C 変数を取得
32 / 39
34. Python から呼び出す関数の引数, 返り値は Python オブジェクト
static PyObject *
func(PyObject *self, PyObject *args) {
const char *s;
if (!PyArg_ParseTuple(args, "s", &s))
return NULL;
...
}
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
PyErr *で例外処理
if (PyErr_Occurred()) {
if (/* StopIteration だったら */) PyErr_Clear();
else { goto __pyx_error; }
}
34 / 39
35. PyObject Call*で Python オブジェクトの呼び出し
PyObject* PyObject_Call(PyObject *callable,
PyObject *args, PyObject *kw)
args, kw のチェック, 再帰の管理などが行われる
ガベージコレクションは参照カウント法
参照カウントの振る舞い “参照の所有権” により理解される
Py INCREF, Py DECREF で参照カウンタを増減
参照カウンタが 0 になったオブジェクトは破棄される
35 / 39
37. まとめ
Cython を使って C/C++ライブラリの Python ラッパーを
“手軽に” 作ることが出来る
拡張ライブラリの仕組みを把握するには
Python/C API の知識が必要
Cython は
C/C++と Python の両方の長所を活かすための
橋渡しとしての役割を果たす
37 / 39