技術メモ

役に立てる技術的な何か、時々自分用の覚書。幅広く色々なことに興味があります。

pyxファイルとは(cythonについて)

他人のソースを動かしていると、.pyxというファイルがあって、これ何?と思ったから調べてみた。

.pyxの拡張子とは、PythonC言語ライクにコンパイルするCythonというメタ言語スクリプトファイルのこと。つまり、Cythonっていうほぼpythonみたいな言語があって、それが書かれたファイルってことらしい。

Cythonについて興味が湧いてきたので少し掘り下げてみた。

Cython は、C言語によるPythonの拡張モジュールの作成の労力を軽減することを目的として開発されたプログラミング言語である。その言語仕様はほとんど Python のものと同じ (上位互換) だが、Cの関数を直接呼び出したり、C言語の変数の型やクラスを宣言できるなどの拡張が行われている。Cython の処理系ではソースファイルを C のコードに変換し、コンパイルすれば Python の拡張モジュールになるようにして出力する。
Wikipediaより

要は、Pythonをcみたいに高速化したい!って時とかcの関数をPythonで使いたい!って時に使うっぽい。


今回は簡単なチュートリアルを試してみて、Cの関数をPythonで使うサンプルを書いてみることにした。
以下、Cythonのチュートリアル

Hello world!

Cythonのビルド

  1. 用意するもの 
    • pyxファイル:Cythonの記法で書かれたスクリプトファイル
    • setup.pyファイル:cythonにおいてMakefileのようなもの
  2. ビルド手順
    • setup.pyを使って.c,.soを生成

実際にやってみる

  • hello.pyx
print('Hello World!')
  • 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"])]
)

この時点ではこの二つのファイルしかない

>>ls
hello.pyx setup.py

以下のコマンドでビルド。

python setup.py build_ext --inplace

すると色んなファイルができている。

>>ls
build/ hello.c hello pyx hello.so setup.py

試しに同じディレクトリでimportを実行してみると...

>>> import hello
Hello World!

できた!

Cのビルトイン関数を使ってみる

cythonでcで書かれた関数を読めるのか疑問に思ったので
cで書かれた関数をpythonでimportするところまでやってみる。

pythonのimportと違ってcthonでcのライブラリをimportするときはcimportを使う。
例えば、math.hを使うときはこのようにする。

  • csin.pyx
from libc.math cimport sin

cdef double c_sin(double x):
     return sin(x)
def get_c_sin(x):
    return c_sin(x)

ちなみに、標準で cimport できるファイルの一覧は、 Cython のソースパッケージの Cython/Includes/ の下を見れば分かる。
Anacondaを使っている環境では
~/anaconda2/pkgs/cython-0.24.1-py27_0/lib/python2.7/site-packages/Cython/Includes/
にあった。
setup.pyはそのまま"csin.pyx"を追加するだけでもいいかと思ったが、
一部のUnixシステムでは数学ライブラリをデフォルトでリンクしないので、共有ライブラリ m へのリンクを設定しなければいけないらしい。
C の関数を呼び出す — Cython 0.17.1 documentation

  • 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("hello",["hello.pyx"]),
                   Extension("csin",["csin.pyx"],libraries=["m"])],
)

さっきと同じようにビルドしてやると…

>>> import csin
>>> csin.get_c_sin(1)
0.8414709848078965

できた!


ちなみに、

>>>csin.c_sin(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'c_sin'

は実行できなかった
cdefで宣言した関数は呼び出せないのか???

参考サイト: