目次へ戻る §14へ戻る §15.1 §15.2 §15.3 §15.4 §15.5 §15.6 §15.7 §16へ進む

15.6 run コマンドによる PySol 起動バッチファイル

ここでは 前節 で導入した PySol を Windows から直接起動できるようにバッチファイルを書いてみます。 同様のことを X11 について §9.3 で行いました。そのときと同じように, ここでも run コマンドを利用します。

run コマンドはコンソール・ウィンドウを隠してプログラムを実行する Cygwin 独特のコマンドです。 Cygwin プログラムのほとんどはコンソール・モード・アプリケーション *14ですから, 本来ならば,標準入出力のためのコンソール・ウィンドウがまとわりつきます。 run コマンドは,このコンソール・ウィンドウを隠して, あたかも GUI モード・アプリケーションのような見かけにします。 run コマンドはもともと Cygiwn 用 X11 プログラムのひとつでした。 しかし,今年 (2005年) 11月に改編され, base カテゴリの独立パッケージになりました。 今は X11 を導入しなくてもデフォルトでインストールされます。
互換性のため,run コマンドは一般コマンド用の /usr/bin と X11 プログラム用の /usr/X11R6/bin の両方にインストールされます。

/usr/local/bin/pysol.bat のようなファイルとして, 下記の内容のバッチファイルを作成します。

@echo off
C:
chdir \cygwin\bin
run bash -l -c 'cd /usr/local/lib/pysol-4.81/src; python pysol.py --nosound'

オプション -l (エル)--login と同義です。 どちらを使っても OK です。 このオプションにより,-c 以降のコマンドに先立ち /etc/profile と ~/.bash_profile が実行され, 環境変数が普段と同じように整えられます。

Cygwin を経由せずに Windows から直接実行しますから,行末は \r\n にします。 cat -etv で次のように行末が表示されることを確かめます。

01:~$ cat -etv /usr/local/bin/pysol.bat
@echo off^M$
C:^M$
chdir \cygwin\bin^M$
run bash -l -c 'cd /usr/local/lib/pysol-4.81/src; python pysol.py --nosound'^M$

§9.3 と同じように, pysol.bat へのショートカットをデスクトップに作成したり, ショートカットに好きなアイコンを貼り付けたりすると良いでしょう。

15.7 拡張モジュールの手ほどき

python の実行効率に不満があるとき, あるいは C 言語経由でしかアクセスできない API があるとき, 拡張モジュールを C 言語で書くことになります。

Python の特長のひとつは,このような場合でも, Python がプラットフォーム間の差異をほとんど吸収してくれることです。 C 言語で書いたモジュールは共有ライブラリとしてコンパイルする必要がありますが, C コンパイラのパス名や必要なオプション,さらにはインストールまでの手間まで Python 標準ライブラリ distutils がほとんど面倒を見てくれます。 ANSI C 規格のライブラリと Python の C 言語用 API だけを利用する限り, どこに持っていってもビルド&インストールできる可搬性の高いモジュールを 実現できます。
話を Unix 類どうし (Cygwin を含む) に限っても, 共有ライブラリのビルド方法は不統一ですから, ライブラリの範囲を ANSI C から Unix 一般に広げてもやはり意義があります。 一度書けばネイティブコードの速度でどこでも動く,という事実は, プログラマにとって魅力的です。

高速 tak 関数のための ctak モジュール

高速化の例として,§15.1の tak 関数を C 言語で書いてみます。 C 言語ですから,モジュール名は ctak とすることにします。 下記のような C プログラムを書きます。ファイル名は仮に ctak.c とします。

#include "Python.h"

static int
tak(int x, int y, int z)
{
    if (y >= x) {
	return z;
    } else {
	return tak(tak(x - 1, y, z),
		   tak(y - 1, z, x),
		   tak(z - 1, x, y));
    }
}

static PyObject*
ctak_tak(PyObject* self, PyObject* args)
{
    int x, y, z;
    if (!PyArg_ParseTuple(args, "iii", &x, &y, &z)) {
	return NULL;
    } else {
	int result = tak(x, y, z);
	return Py_BuildValue("i", result);
    }
}

純粋に C 言語で書いた tak 関数と, その引数と結果の値を python とのあいだで受け渡すための ctak_tak 関数です。 PyArg_ParseTuple 関数は,"iii" というフォーマット引数で Python から 三つの整数を受け取ります。 Py_BuildValue 関数は,"i" というフォーマット引数で, 1個の整数を表現する Python オブジェクトを構築します。

static PyMethodDef
ctak_methods[] = {
    {"tak", ctak_tak, METH_VARARGS,
     "Execute `tak' function: (int, int, int) -> int"},
    { NULL, NULL, 0, NULL}
};

void
initctak(void)
{
    Py_InitModule("ctak", ctak_methods);
}

C 言語のモジュールが python に読み込まれるとき, "init" + モジュール名 という名前の, モジュールの外部関数が実行されます。 そこでは普通,Py_InitModule 関数で初期化をします。 "ctak" モジュールは, ctak_methods 配列経由で ctak_tak 関数を "tak" という名前で登録します。 python からは import ctak 後,ctak.tak という名前で, C 言語の ctak_tak 関数を参照できることになります。 (だから ctak_tak と命名したわけです)

setup スクリプト

ctak.c ファイルと同じディレクトリに下記の Python スクリプトを作ります。 名前は setup.py とします。

from distutils.core import setup, Extension

setup(name="tak-in-c",
      version="1.0",
      ext_modules=[Extension('ctak', ['ctak.c'])])

シェルから python setup.py と実行すれば,簡単なヘルプが出ます。 標準モジュール distutils.core の setup 関数は,自分自身への引数と, コマンド行引数の両方から動作を決定します。 C コンパイラを実行し,共有ライブラリを構築するには, python setup.py build を実行します。 setup.py の記述に従い, ctak というモジュールを ctak.c というソースから構築します。

01:~/tmp/python$ python setup.py build
running build
running build_ext
building 'ctak' extension
creating build
creating build/temp.cygwin-1.5.18-i686-2.4
gcc -fno-strict-aliasing -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -I/usr/includ
e/python2.4 -c ctak.c -o build/temp.cygwin-1.5.18-i686-2.4/ctak.o
creating build/lib.cygwin-1.5.18-i686-2.4
gcc -shared -Wl,--enable-auto-image-base build/temp.cygwin-1.5.18-i686-2.4/ctak.
o -L/usr/lib/python2.4/config -lpython2.4 -o build/lib.cygwin-1.5.18-i686-2.4/ct
ak.dll
01:~/tmp/python$ ls
build/  ctak.c  setup.py
01:~/tmp/python$ ls build/lib.cygwin-1.5.18-i686-2.4/
ctak.dll*
01:~/tmp/python$

構築に成功したようですから,試しに動かしてみます。

01:~/tmp/python$ cd build/lib.cygwin-1.5.18-i686-2.4/
01:~/tmp/python/build/lib.cygwin-1.5.18-i686-2.4$ python
Python 2.4.1 (#1, May 27 2005, 18:02:40)
[GCC 3.3.3 (cygwin special)] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctak
>>> help(ctak.tak)
Help on built-in function tak in module ctak:

tak(...)
    Execute `tak' function: (int, int, int) -> int

>>> ctak.tak(4, 2, 0)
1
>>> ctak.tak(18, 12, 6)
7
>>> ctak.tak(30, 10, 5)
6
>>>

実験に使った Pentium M 1.0 GHz のマシン (東芝 Portege R100) では, 上記の例は Enter キーの打鍵とともに計算が一瞬で終わって結果がでました。 最後の例は §15.1 の Python 版 tak 関数では少し計算時間がかかりますが, それもここでは一瞬です。

setup スクリプトについての補足

01:~/tmp/python$ python setup.py install

とすると,もしもまだ build していなければ build した後, 結果の ctak.dll をサイト独自のモジュールの置き場である /usr/lib/python2.4/site-packages/ にインストールします。

01:~/tmp/python$ python setup.py sdist

とすると,dist/ ディレクトリを作成し,そこに ctak.c と setup.py を含んだソース配布用アーカイブ・ファイル tak-in-c-1.0.tar.gz を作成します。 (setup.py で setup 関数に与えた name 引数と version 引数がここで使われます)

おわりに

C 言語でモジュールを書くとき, 一般的な Python/C API 関数を利用しただけで, プラットフォーム固有の知識をほとんど何も必要としなかったことを 思い起こしてください。C コンパイラの名前さえ知る必要はありませんでした。

もちろん,これは最も簡単な例にすぎず,現実の事例では, 外部ライブラリへの参照を追加する必要がよくありますが, やりかたはワンパターンです。 Extension() の引数で libraries=['ライブラリ名', ...] と library_dirs=['ライブラリのあるディレクトリ', ...] を指定するだけです。 詳細は /usr/share/doc/python-2.4.1/html/dist/index.html を参照してください。

Python で本格的なプログラムを作成するとき, まず純粋な Python 言語で軽くあたりをつけ,全体の構成を確立した後, 性能上のネックになるところを C 言語で置き換えるとよいわけですが, これは,Python インタープリタの性能ではすべてをカバーすることができない, という言い訳だけではありません。 実際に,誰でも一般的な素養と当該問題に対する専門知識だけあれば, プラットフォーム独自の雑知識を頭に詰め込まなくても, C 言語への部分置換を遂行できるだけのお膳立てを, Python は現実のツールとして用意しているわけです。

とりわけ前例を利用できない挑戦的な問題の場合 (それはしばしば個人にとって興味あるものであり, 組織にとって競争力の源泉となるものですが), 適切な抽象化を行うことが, その後のプログラミングの成否を決定する大きな要因になります。 抽象化の段階で十分な試行錯誤を行うことが必要ですが, 通常の言語ではコストがかかりすぎることが,それを机上の空論にしてきました。 Python のような,試行錯誤のコストが低い超高水準言語 (別名,実動する疑似コード) は, このような抽象化のためのツールとして有用です。 C 言語への置換えは,一見すると二度手間のようですが, すでに抽象化の作業が済んでいるため,実際には大部分が単純な翻訳作業 (=狭義の「コーディング」) に簡約されます。 中核的な問題を解決するために必要な頭を浪費せずに手軽に済むわけです。


脚注

*14 あまり一般的ではありませんが,Cygwin で Windows ネイティブな GUI モード・アプリケーションを作成することもできます。 通常の Windows 用 C 言語の場合と同じく WinMain 関数を定義し, gcc に -mwindows オプションを与えてコンパイルします。 Windows リソースは /usr/bin/windres コマンドで処理します。 詳細については, cygwin-doc パッケージ (§3.1, §14.6) に含まれる Cygwin User's Guide の Chapter 4. Programming with Cygwin を御覧ください。

目次へ戻る §14へ戻る §15.1 §15.2 §15.3 §15.4 §15.5 §15.6 §15.7 §16へ進む

Copyright (c) 2005 Oki Software Co., Ltd.