チュートリアル基礎編

Cython の基礎

Cython の本質を一言で表すなら、「Cython とは C のデータ型を持った Python である」といったところでしょう。

Cython は、まるで Python のように動作します。Python のコードは、ほとん どが Cython のコードとして使えます (いくつか Cython の制約 はありますが、現時点では、ほぼ同じといえます)。 Cython のコンパイラは Python のコードを C のコードに変換し、等価な Python/C APIへの呼び出し に変えます。

しかし、 Cython のできることはそれだけではありません。パラメタや変数に C のデータ型を持たせられるのです。 Python の値と C の値を操作するコー ドを自在に組み合わせることができ、それらの間に互換性があれば、自動的に 変換を行います。Python における参照カウントの管理やエラーチェックも自 動的に行われ、C データの操作中でさえ、 try-except や try-finally 文を 含む Python の例外処理の力をあますところなく活用できるのです。

Cython の Hello World

Cython は常に有効な Python のソースファイルを受け取るので、 Cython 入 門で難しいところといえば、拡張モジュールのコンパイル方法くらいでしょう。

というわけで、まずは正典たる Python の hello world からはじめましょう:

print "Hello World"

最初にやることは、ファイルのリネームです。 helloworld.pyx にし ます。次に、 setup.py を作成します。 setup.py は Python における Makefile のようなものです。 (詳しくは ソースファイルとコンパイル を参照してください) setup.py には以下のよう に記述します:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("helloworld", ["helloworld.pyx"])]
)

この setup.py を使って Cython ファイルをビルドするには、以下の ようなコマンドラインオプションを使って実行します:

$ python setup.py build_ext --inplace

このコマンドを実行すると、手元のディレクトリに、 unix 系の OS であれば helloworld.so, Windows であれば helloworld.dll という ファイルが生成されます。今度はこのファイルを使ってみます。 Python イン タプリタを起動して、ファイルを普通の Python モジュールと同様に import してみます:

>>> import helloworld
Hello World

おめでとう!これで、 Cython の拡張モジュール作成法を会得しましたね。と はいえ、この例だけでは、 Cython を使ってみようという気にはなれないかも しれません。そこで、もうちょっと現実的な例を挙げていくことにします。

pyximport: Cython の簡単コンパイル

モジュールをビルドするときに、外部の C ライブラリや特殊なビルド設定を 必要としないのであれば、Paul Prescod と Stefan Behnel の作成した pyximport モジュールを使って、 .pyx ファイルを直接 import できます。 setup.py ファイルを書く必要はありません。 pyximport は Cython に付属していて、一緒にインストールされます。 pyximport は下記のようにして使います:

>>> import pyximport; pyximport.install()
>>> import helloworld
Hello World

Cython 0.11 からは、 pyximport モジュールで通常の Python モジュー ルをコンパイルできるようにもなりました。この機能によって、 .pyx ファ イルだけでなく、Python から import できる、標準ライブラリやパッケージ も含む .py ファイルも自動的に import できるようになりました。 Cython のコンパイルできない Python モジュールはまだ沢山ありますが、その場合に は import メカニズムが Python のソースモジュールをロードするようフォー ルバックします。 .py ファイルの import 機構は、下記のようにしていイ ンストールします:

>>> pyximport.install(pyimport = True)

フィボナッチファン

Python の公式チュートリアルには、以下のような簡単なフィボナッチ関数の 定義があります:

def fib(n):
    """Print the Fibonacci series up to n."""
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a + b

Hello World の例に従って、まずファイルの拡張子を .pyx に変えて、 fib.pyx にしましょう。次に、 setup.py ファイルを作成し ます。 Hello World の例で作成した setup.py をもとにすれば、 Cython のファイル名と出力ファイルを変更するだけで済みます。変更後のファ イルは以下のようになるでしょう:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("fib", ["fib.pyx"])]
)

helloworld.pyx のときと同じコマンドで、拡張モジュールをビルド します:

$ python setup.py build_ext --inplace

新しい拡張モジュールを使ってみましょう:

>>> import fib
>>> fib.fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

素数

Cython でできることを表した、簡単な例を示しましょう。このルーチンは素 数を列挙します。素数をいくつ欲しいか指定すれば、Python のリストとして 素数の列を返します。

primes.pyx:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def primes(int kmax):
    cdef int n, k, i
    cdef int p[1000]
    result = []
    if kmax > 1000:
        kmax = 1000
    k = 0
    n = 2
    while k < kmax:
        i = 0
        while i < k and n % p[i] != 0:
            i = i + 1
        if i == k:
            p[k] = n
            k = k + 1
            result.append(n)
        n = n + 1
    return result

関数の冒頭は通常の Python の関数定義と同じなのに、パラメタ kmaxint 型で定義されているのに気づいたでしょうか。これは、引数に渡した オブジェクトを C の int 型に変換する (変換できないときは TypeError を送出する) という意味です。

2 行目と 3 行目では、 cdef 文を使ってローカルな C の変数を定義して います。 4 行目では、戻り値を返すための Python のリストを作成していま す。このあたりは、 Python で書く時と全く同じだということにお気づきでしょ う。変数 result には型を指定していないので、 Python のオブジェクト を保持するための変数とみなされます。

7-9 行目ではループをセットアップして、必要な数だけ素数が得られるまで、 候補の数が素数であるか調べています。 11-12 行目は、候補の数をこれまで に見つかった全ての素数で除算する肝の部分です。Python のオブジェクトは 参照されないので、ループは完全に C のコードに翻訳され、極めて高速に動 作します。

素数が見つかると、 14-15 行目で配列 p に追加します。これは、素数テ ストのループを高速に回すための処置です。 16 行目では、戻り値のリストに も追加しています。 16 行目も、 Python の文とよく似ていますね。実際その 通りで、さらに、ここでは C のパラメタ n が自動的に Python のオブジェ クトに変換されてから append メソッドに渡されています。最後に、 18 行目で、通常の Python の return 文でリスト result を戻 り値として返しています。

Cython コンパイラで primex.pyx をコンパイルすれば、拡張モジュー ルがシエ性されます。インタプリタで試してみると、以下のようになるでしょ う:

>>> import primes
>>> primes.primes(10)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

ほら、うまく動きましたね! Cython が拡張モジュールの作成をどう手助けし てくれたかを知りたければ、モジュールの C コードを見てみるとよいでしょ う。

Cython の詳しい言語仕様

Cython の詳しい言語仕様は 基本の言語仕様 を参照してください。 Cython を使って数値計算の世界に飛び込みたければ Cython for NumPy users を読んでみて下さい。