Cython と Pyrex の違い

Warning

Cython も Pyrex も、いわばムービング・ターゲットです。 二つのプロジェクトの違いを系統立てたリストを作成し、追跡していくの は困難な作業になってきています。しかし、おそらくこのリストを見れば、 現状での違いは理解できることでしょう。 二つのプロジェクトは、互いに互換性を持たせようと努力していますが、 Cython のゴールは、あくまでも可能な限り Python と同じ書き方で、 Python の動作を完璧に再現することにあります。

Python 3.0 サポート

Cython は .c ファイルを作成します。このファイルは Python 2.x でも Python 3.x でもビルドして使えます。実際、モジュールを Cython でコンパ イルするのが、コードを Python 3.0 に移植する一番簡単な方法といえるでしょ う。また、開発者たちは、 Python 2.x と 3.0 の両方で動作するコンパイラ の開発に取り掛かっています。

Cython は、すでに Python 3 の構文コンストラクトの多くをサポートしてい ます。

list/set/dict の内包表記

Cython は、 list, set, dict それぞれ固有の内包表記をサポートしています。 これらの内包表記は、 Python 3.0 で定義されました:

[expr(x) for x in A]             # list
{expr(x) for x in A}             # set
{key(x) : value(x) for x in A}   # dict

A がリスト、タプル、辞書の場合には、Cython はループ処理を最適化し ます。 for ... from 構文も使えますが、一般的に は、 for ... in range(...) 構文を C の実行変 数 (cdef int i) と一緒に使うよう勧めます。

Note

range の自動変換 を参照してください。

Cython は Python 2.3 から登場した set のリテラルもサポートしています。

キーワード (のみの) 引数

Python の関数は、 * パラメタよりも後かつ ** パラメタよりも前に、 キーワード形式のみで指定できる引数を持たせられます。例えば:

def f(a, b, *args, c, d = 42, e, **kwds):
    ...

ここで、 c, d, e は位置指定引数 (positional argument) とし て渡すことはできず、キーワード引数として渡さねばなりません。また、 ce はデフォルト値を持たないため、必須の引数です。

* 以降のパラメタ名を省略すると、関数は追加の位置指定引数を受け取ら なくなります。例えば:

def g(a, b, *, c, d):
    ...

は二つの位置固定引数と、二つの必須のキーワード引数だけを取ります。

条件式 (conditional expression) “x if b else y” (python 2.5)

http://www.python.org/dev/peps/pep-0308/ で解説している条件式:

X if C else Y

は、 (C の値に応じて) X または Y のどちらかを評価します。

cdef inline

inline キーワードを C コンパイラに渡して、モジュールレベル 関数を inline で宣言できるようになりました。 inline の関数はマクロと同 じくらい高速です:

cdef inline int something_fast(int a, int b):
    return a*a + b

クラスレベルの cdef 関数は仮想関数テーブルで扱うため、ほと んどの場合、コンパイラは inline 化を行えないので注意してください。

宣言時代入 (“cdef int spam = 5”)

Pyrex では、以下のような書き方をせねばなりません:

cdef int i, j, k
i = 2
j = 5
k = 7

Cython では、下記のように書けます:

cdef int i = 2, j = 5, k = 7

右辺の式は好きなだけ複雑にできます。例えば:

cdef int n = python_call(foo(x,y), a + b + c) - 32

for ループの ‘by’ 節 (“for i from 0 <= i < 10 by 2”)

下の例:

for i from 0 <= i < 10 by 2:
    print i

のように書くと:

0
2
4
6
8

になります。

Note

Python の for ループと重複するので、この構文の利 用はあまりおすすめしません。 range の自動変換 を参照してください。

ブール整数型 (C の int に類似で、 Python のブール型との間で型強制するもの)

C では、真偽値の表現に整数を使います。 Python では、どんなオブジェクト も (__nonzero__() メソッドを使って) 真偽値を表現できます。ただし、 真偽値を正式に表現するなら、二つのブール値オブジェクト、 TrueFalse を使います。 bint (“boolean int”) 型は、コンパイル すると C の int 型ですが、 Python のブール型との間で型強制します。比較 演算や、一部のビルトイン関数の戻り値も bint です。 そのため、 bool() でラップする必要がありません。例えば:

def is_equal(x):
    return x == y

この関数は、 Pyrex では 10 を返しますが、 Cython では TrueFalse を返します。変数や関数の戻り値も、 bint 型で宣言できます。例えば:

cdef int i = x
cdef bint b = x

上の例中の最初の変換には x.__int__() を使いますが、二つ目の変換は x.__nonzero__() を使います。 (実際には、 x が Python のオブジェ クト TrueFalse の場合、メソッド呼び出しを行いません)。

クラスボディでの実行

classmethod() も正しく動作します:

cdef class Blah:
    def some_method(self):
        print self
    some_method = classmethod(some_method)
    a = 2*3
    print "hi", a

cpdef 関数

Cython には、 defcdef に加えて、第三の関数型 があります。関数を cpdef で宣言すると、拡張モジュールと Python の両方から呼び出せます。また、メソッドの場合は、拡張モジュールと Python の両方のサブクラスでオーバライドできます。 cpdef は、 本質的には cdef に若干手を加えたもの (少なくとも、実装はそ のようになっています) です。まず、 cdefdef メソッドを生成します。このメソッドは水面下にある cdef を呼 ぶだけです (必要に応じて、引数のアンパックや型強制を行います)。 cdef メソッドの上に、ほんのちょっとだけコードが乗っていて、 メソッドがオーバライドされているかどうかをチェックします。擬似コードで 書くと、下記のようになります:

if type(self) has a __dict__:
    foo = self.getattr('foo')
    if foo is not wrapper_foo:
        return foo(args)
[cdef method body]

型が辞書を持つかどうかを調べるには、 tp_dictoffset スロットの値を調べ ます。拡張型では NULL (デフォルト値) で、インスタンスクラスでは非 NULL です。辞書があれば、アトリビュートをルックアップして、その結 果を (ポインタと比較して) 新しい関数かどうかを調べます。新しい関数であ れば、また、そのときに限り、引数をタプルにパックして、メソッドを呼び出 します。この一連の処理は、極めて高速です。クラスから直接メソッドを呼び 出す場合には、特殊なフラグがセットされ、ルックアップは起こりません。 従って:

cdef class A:
    cpdef foo(self):
        pass

x = A()
x.foo()  # オーバライドされているかどうかチェックする
A.foo(x) # オーバライドの有無に関わらず、Aの実装を呼び出す

解説や利用上の注意は アーリーバインディングによる高速化 を参照してください。

range の自動変換

Cython では、 for i in range(...) 文のループ変数 i が cdef さ れていて、ループの方向 (ステップの符号) が決まっていると、自動的に for i from ... に変換します

Warning

この変換は、 range が i にオーバフローするような値を代入してし まう場合のセマンティクスを変えてしまうことがあります。 具体的には、あるオプションをセットすると、オーバフローするような range はループに入る前に例外を送出し、オプションをセットしなければ、 オーバフロー値に至るまでループを実行します。 挙動を変えたければ、 Cython/Compiler/Options.py を変更してくだ さい (将来的には、もっとましな設定方法になるはずです)。

よりフレンドリーな型キャスト

Pyrex では、Python のオブジェクト x に対して <int>x とすると、 x のメモリアドレスを取得します。同様に、 i が C の int のとき に <object>i とすると、メモリ上の i で示される場所に「オブジェ クト」ができます。この仕様は混乱の元で、セグメンテーション違反を引き起 こしがちです。

Cython では、 <type>x とした場合、式中のいずれかの型が Python のオ ブジェクトであれば、 (type 型の変数に x を代入したかのように) 型強制を試みます。変換が起きない場合でも (警告を出力しますが) 明示的 なキャストが可能です。メモリアドレスがどうしても必要なら、まず void * にキャストします。

Pyrex の場合と同様、Cython でも <MyExtensionType>x とすると、型チェ ックを行わずに、 xMyExtensionType 型にキャストしま す。 加えて、 Cython では、 <MyExtensionType?> という構文をサポートして います。この構文では、型チェック付きのキャストを行います (つまり、 xMyExtensionType 型 (またはサブクラス) の時にはエラー を送出します)。

cdef/cpdef 関数のオプション引数

Cython は cdefcpdef 関数のオプション引数をサ ポートするようになりました。

.pyx ファイル中の構文は基本的に Python と同じですが、 .pyd ファイルの中では cdef foo(x=*) と宣言します。 引数の数はサブクラスで増やしても構いませんが、引数の型と順番は同じにせ ねばなりませんん。オプション引数を持たない cdef/cpdef 関数をデフォルト 引数値を持つ関数でオーバライドすると、パフォーマンス上のペナルティを負 うことがあります。

例えば、以下のような .pxd ファイルを書いて:

cdef class A:
    cdef foo(self)
cdef class B(A)
    cdef foo(self, x=*)
cdef class C(B):
    cpdef foo(self, x=*, int k=*)

対応する .pyx ファイルを以下のように書けます:

cdef class A:
    cdef foo(self):
        print "A"
cdef class B(A)
    cdef foo(self, x=None)
        print "B", x
cdef class C(B):
    cpdef foo(self, x=True, int k=3)
        print "C", x, k

Note

上の例では、 cpdef 関数で cdef 関数をオーバ ライドする方法も示しています。

構造体中の関数ポインタ

struct の中で関数を宣言すると、自動的に関数ぽいインタに変換 します。

C++ の例外処理

cdef 関数を、下記のように宣言できます:

cdef int foo(...) except +
cdef int foo(...) except +TypeError
cdef int foo(...) except +python_error_raising_function

上のように宣言すると、C++ のエラーをキャッチしたときに Python の例外を 送出します。詳しくは Cython から C++ を使う を参照してください。

シノニム

cdef import fromcdef extern from と同じ意味を持ちます。

ソースコードのエンコーディング

Cython は PEP 3210 と PEP 263 をサポートしています。つまり、 Cython の ソースコードファイルの先頭にエンコーディング指定の行を入れたり、エンコー ディングを省略して UTF-8 でコードを書いたりできます。この仕様は、バイ ト文字列をエンコードするときや、 u'abcd' のような Unicode リテラル を unicode オブジェクトに変換するときに影響します。

自動 typecheck

Pyrex では新たなキーワード typecheck を導入しています ( Pyrex ドキュメント 参照) が、Cython は、拡張型を isinstance() の第二パラメタに指定 したときは常に (偽装不可で、より高速な) 型チェックを行います。

from __future__ ディレクティブ

Cython は unicode_literalsdivision といった from __future__ ディレクティブをサポートしています。

with 文は常にサポートしています。

pure Python モード

Cython は、 .py ファイルのコンパイルをサポートしています。また、デ コレータなど、正しい Python の構文で型をアノテーションできます。 そのため、同じコードをそのまま Python で実行でき、同時にコンパイルして 最適化もできます。詳しくは http://wiki.cython.org/pure を参照してくだ さい。