[PR]今日のニュースは
「Infoseek モバイル」


Jythonとは?

Jythonは、Java言語で書かれたPython言語のインタープリタ(解釈実行系)です。

それだけ?
そんなことはありません。Jythonを使うと、Pythonのスクリプトの中から JavaのクラスをPythonのクラスと同じように使うことができます。 この、JavaのクラスをPythonのクラスと同様に使える、というのがたいへん面白いところです。 「そんな変なことするんだから、書き方がきっとトリッキーなんじゃないの?」 と疑り深い皆さんも心配ゴム用。 できあがったJythonのスクリプトを見ても、 どれがJava起源のクラスか全く意識せずとも使える程シームレスフリャに融合が図られています。

JythonはPython言語(と、そのC言語で書かれたインタープリタ) の進歩に追随するように開発が進んできました。 Python 1.6まではJPythonという名前でしたが、Python 2.0 の登場に合わせて、Jythonと名前を変えた新プロジェクトとなりました。 現在の総本山はwww.jython.orgで、 このサイトから最新のバージョン2.1が無償で入手できます。 リリース履歴はこのようになっています:

なお、Jythonは100%純粋なJava言語で書かれているので、 MS-WindowsでもUNIXでも同じように使うことができます

Jythonを使うには、Javaのランタイム(JRE)または開発環境(JDK) を先にインストールしておく必要があります。


Jython 2.1のインストール

次の環境で試しました。

プラットフォームJavaJython
MS-Windows 95JDK 1.1.72.1a1
MS-Windows 98J2SE 1.2.22.1a2
MS-Windows XPJ2SE 1.42.1
MS-Windows XPJ2SE 1.4.2_012.2a0
Linux Kernel 2.2.14J2SE 1.32.1a1

JythonはインストーラもJavaなので、 WindowsでもUNIXでもインストール作業はほとんど共通です。
  1. Jythonの総本山から、 jython-21a1.classをダウンロードします。 この.classファイルが自己解凍インストーラになっています。 ダウンロードしたら、このファイルがあるディレクトリに移動します。
  2. マシンにインストール済みのJavaのjavaコマンドを使って、
    % java jython-21a1
    
    とするとインストール画面が現れます。 インストールパスは /usr/local/jython-2.1a1 などと適当に入力すればよいでしょう。 インストール自体は全自動で完了するはずです。
    サーバー機などの場合で、ウィンドウを出すことができない場合には、
    % java jython-21a1 -o /export/home/watasi/local/jython
    
    のようにすると、インストール画面を出さずに全自動でインストールが進みます。 このように、 インストール位置はJython専用のディレクトリにするのが分かりやすいと思います。
  3. 上で指定したインストールディレクトリのbinサブディレクトリに、 Windowsなら「jython.bat」が、UNIXならシェルスクリプト「jython」があるはずです。 これは普通のpythonコマンド(python.exe)のように使うことができるインタープリタです。 さっそく使ってみましょう。

    % /usr/local/jython-2.1a1/jython
    *sys-package-mgr*: processing new jar, '/usr/local/jython-2.1a1/jython.jar'
    *sys-package-mgr*: processing new jar, '/usr/local/pgsql/lib/postgresql.jar'
    *sys-package-mgr*: processing new jar, '/usr/local/jdk1.3/jre/lib/rt.jar'
    *sys-package-mgr*: processing new jar, '/usr/local/jdk1.3/jre/lib/i18n.jar'
    *sys-package-mgr*: processing new jar, '/usr/local/jdk1.3/jre/lib/sunrsasign.jar'
    Jython 2.1a1 on java1.3.0 (JIT: null)
    Type "copyright", "credits" or "license" for more information.
    >>>
    >>>3+12
    15
    >>>
    

    このように、Jythonは最初にインストールされているJARファイルを検索し、 前回実行時から増えているJARファイルを見つけたら 「...processing new jar...」、前回実行時から修正されているJARファイルを見つけたら 「...processing modified jar...」というメッセージを表示します。 なお、jython.batやjythonシェルスクリプトは Jythonのインストールディレクトリに必ずしも置かなくてもよくて、 パスが通っている /usr/local/bin などにコピーするか、リンクを張るなどしてもよいでしょう。

    "C:\jdk1.4\bin\java.exe" "-Dpython.home=C:\Wintools\jython-22a0" \
     -classpath "C:\Wintools\jython-22a0\jython.jar;%CLASSPATH%" \
     org.python.util.jython %ARGS%
    

    の部分を書き換えれば、インストールディレクトリを移動することも可能です。


普通のPythonスクリプトを実行する

 プログラム「jython」は「python」と全く同様に使えます。 当然、同じPythonスクリプトを引数として実行すれば、ほぼ同じ結果を得ることができます。

# sample.py
a = 'Hello, ' + 'World'
print a

例えば上のようなスクリプトなら、コマンドラインから

% jython sample.py
Hello, World
%

このように実行することができます。

timeやstringなど、主だったモジュールについても、Python環境と同じように使えます。 例えば「time」モジュールについては、

import time
tm = time.localtime(time.time())
tmstr = time.strftime("%Y/%m/%d %H:%M:%S", tm)
print "now: " + tmstr

このように、Pythonのときと全く同じようにJythonでもimportとして、 同じ結果を得ることができます。 ただし、C言語版のPythonでC言語で書かれた(=共有ライブラリとして提供されている) バイナリモジュールは、Java言語への移植が必要です。 これらのサポート状況は、Jythonホームページの情報やMLの過去ログで確認が必要です。

PythonとJythonの相違点で最も要注意なのは、ファイルI/Oです。 Javaは日本語などアルファベット以外の文字列をUTF-8に変換して内部で管理しますが、 Pythonはべたの文字コードのまま、つまり単なるバイトの並びとして持っています。 その違いが禍いしてか、下のようにPythonのファイルI/Oを使うと日本語の文字列は化け化けになってしまいます。

import sys, string
try:
    infile = sys.argv[1]
except IndexError, e:
    print "おおっと:", e
    sys.exit(0)
try:
    fin = open(infile, "r")
except IOError, e:
    print "おおっと:", e
    sys.exit(0)
buf = fin.readline()
while buf != "":
    print buf.strip()
    buf = fin.readline()
fin.close()
# end.


Javaの標準クラスを使う

 さてここからが本番です。Jythonの本領発揮、ということで、 JavaのクラスをJythonから呼び出して使ってみます。 最初はJ2SEの標準API、次に自作Javaクラスのロードに挑戦してみましょう。

import java
from java.util import StringTokenizer

st = StringTokenizer('Alpha,2001,日本語EUC,aloha,+_+;6', ',')
while st.hasMoreTokens():
    t = st.nextToken()
    print '[' + t + ']'
print 'done.'
# end.

 この超簡単なPythonスクリプトは、標準Javaクラス java.util.StringTokenizerをインスタンス化して使っています。 これだけの例ですが、幾つかのことがご紹介できます。それは、

つまり、Javaの
import java.util.StringTokenizer;
は、Pythonでは
from java.util import StringTokenizer
と対応します。(この例では出ていませんが、「*」もちゃんと使えます) また、インスタンス化はJavaでは
StringTokenizer st = new StringTokenizer("...");
とやるのをPythonでも同様に
st = StringTokenizer("...")
と、Pythonクラスのインスタンス化と全く変わるところはありませんし、 Java言語の文法ともきっぱり1対1に対応していることがお分かりかと思います。

今度の例は、先程問題があるといったファイルI/Oを、 それならば、Java言語のAPIでやってみようというものです。

import sys, string
import java
from java.io import *

try:
    infile = sys.argv[1]
except IndexError, e:
    print "おおっと:", e
    sys.exit(0)

try:
    fin = FileReader(infile)
    reader = BufferedReader(fin)
except FileNotFoundException, e:
    print "おおっと:", e
    sys.exit(0)

buf = reader.readLine()
while buf != None:
    print java.lang.String.trim(buf)
    buf = reader.readLine()

reader.close()
fin.close()
# end.

今度の例も小さいですが、また幾つかのことをご紹介できます。

今度の例はファイルに日本語の文字列が書かれていても正しく出力されるはずです。 なお、Pythonのprint文に対応するJavaの書き方として、
java.lang.System.out.println("もももも")
という方法もあります。 Pythonのprint文を使うか、JavaのSystem.outを使うかはどちらでも構わないでしょう。

 もうひとつ、先の例はjava.io.FileReaderを使っているので、 システムのデフォルトのエンコーディングで書かれたテキストファイルしか読めませんが、 これをjava.io.InputStreamReaderを使うことで他のエンコーディングで書かれたテキストファイルも読めるように改造したものです。

import sys, string
import java
from java.io import *

#encoding = "EucJP"
encoding = "UTF8"

try:
    infile = sys.argv[1]
except IndexError, e:
    print "おおっと:", e
    sys.exit(0)

try:
    reader = BufferedReader(
      InputStreamReader(FileInputStream(infile), encoding)
    );
except FileNotFoundException, e:
    print "おおっと:", e
    sys.exit(0)

buf = reader.readLine()
while buf != None:
    print java.lang.String.trim(buf)
    buf = reader.readLine()

reader.close()
# end.


自作クラスを使う

 JavaとPythonの効果的な分業を目指す、そんな未来のために今、2人の漢(をとこ)が立ち上がった… 青年誌の吊り広告みたいに始めてしまいましたが、この辺りからだんだん深みにはまっていきます。 愛、勇気、そして夜食を用意して、進んでいきましょう。(はむはむはむ)

/* Address.java */
package addr;
/** 名前、メールアドレス、URLを管理する簡単なクラスです。*/
public class Address {
    String name;
    String mailAddress;
    String url;

    public Address(String name, String mailAddress, String url){
        this.name = name;
        this.mailAddress = mailAddress;
        this.url  = url;
    }

    public String getName(){ return this.name; }
    public String getMailAddress(){ return this.mailAddress; }
    public String getUrl(){ return this.url; }
    public void setMailAddress(String mailAddress){
        this.mailAddress = mailAddress;
    }
    public void setUrl(String url){ this.url = url; }
}

まずはJava言語の簡単なクラスです。これはJythonで使うからといって別段配慮は不要です… と書こうとしましたが一点だけ。Jythonでインスタンス化したいクラスは、 必ずpublicクラスとして作らないといけません。 それ以外はあくまでJava風味のJavaクラスとしてJavaっぽく書けばOKです。 作れたら、CLASSPATHの通っているところに.classファイルを置くか、JARアーカイブにまとめておきます。

import java
from addr import Address

a = Address('西雲花子', 'hanaco@nsnhnkmmkk.co.jp',
            'http://www.nsnhnkmmkk.co.jp/~hanaco/')
url = a.getUrl()
print a.getName() + 'のホームページURLは[' + url + ']です。'
a.setUrl('http://home0.nsnhnkmmkk.co.jp/~hanaco/')
url = a.getUrl()
print a.getName() + 'のホームページURLは[' + url + ']です。'

 そしてPythonのスクリプトです。先程のStringTokenizerや BufferedReaderのときと同様です。Javaクラスをパッケージに入れた場合には、

import the.name.of.package.Address
のように書くか、
from the.name.of.package import Address
のように書きます。後者だとインスタンス化の際にパッケージ修飾が不要になるので、 通常はこちらを使います。

 さてこのPythonスクリプトは、下のようにもっと簡単に書くことができます。

import java
from addr import Address

a = Address('西雲花子', 'hanaco@nsnhnkmmkk.co.jp',
            'http://www.nsnhnkmmkk.co.jp/~hanaco/')
url = a.url
print a.name + 'のホームページURLは[' + url + ']です。'
a.url = 'http://home0.nsnhnkmmkk.co.jp/~hanaco/'
url = a.url
print a.name + 'のホームページURLは[' + url + ']です。'

ありゃ?メソッドコールが大幅に減り、単なる変数への代入になってしまっています。 ひとつずつ見ていきますと、

url = a.getUrl()
url = a.url
に、同じ例では
print 'URL of ' + a.getName() + ...
print 'URL of ' + a.name + ...
また、
a.setUrl('http://...')
a.url = 'http://...'
ここでピンとくる方もおられるかもしれませんね。 Jythonは、JavaBeansのget&setメソッドをプロパティ変数とみなした代入の形に書く簡略表記をサポートしています。 これだけでは、「なんだ少々タイプ数が減っただけじゃねーか」 と思われるかもしれませんが、実はまだまだ。 簡略表記についてはこの後もっと大物をご紹介することになると思います。


Javaのクラスを継承する

 今度は、ですけど、Javaのクラスを継承してPythonのクラスを作るという、 なんか日本語で書くとすごそうですけど、実物を見ればそれほどでもない、

import java
from java.awt import *
from java.applet import *

class HelloApplet(Applet):

    def __init__(self):
        f = Font("Helvetica", 0, 14)
        la = Label("ハローワールド")
        cmda = Button("OK", actionPerformed=self.close, font=f)
        self.add(la)
        self.add(cmda)

    def close(self, event):
        java.lang.System.exit(0)

f = Frame("Hello World Example")
f.setSize(Dimension(100, 50))
my = HelloApplet()
my.setSize(Dimension(100, 50))
f.add(my)
f.show()
# end.

思わずアプレットにしてしまいましたが、普通にローカル機で実行できます。 いやそんなことより奥さん!本題としては、Javaの

class HelloApplet extends Applet { ... }
が Python でも
class HelloApplet(Applet):
と書けるんですよ!とか、 でもJavaクラスからは単一継承しかできないんですよ、 とかあるのですけど、 そんなことはどうでもええねん(いや、結構重要なんですけどね)。 それよりもコンストラクタとリスナーの形を見てください。
cmda = Button("OK", actionPerformed=self.close, font=f)
まず最後のfont=ですが、これは
cmda = Button("OK", actionPerformed=self.close)
cmda.setFont(f)
の簡略記法です。 これは前にも出て来たBeanメソッドをプロパティ変数のように表現するもので、 JythonのマニュアルによるとPython/Tkinterの文法にヒントを得たそうです。 なるほどなるほど、確かにTkinterによく似ていますね。
 そして大物!Javaではイベントリスナーは、それぞれ別々のクラスを書いて、 それをインスタンス化する必要があって、下のように書く必要があるのですが、
    class HelloAppletElisA implements ActionListener {
        public void actionPerformed(ActionEvent ev){
            System.exit(0)
        }
    }

    cmda = Button("OK")
    cmda.setFont(f)
    cmda.addActionListener(new HelloAppletElisA());
これがJythonになると
    def close(self, event):
        java.lang.System.exit(0)

    cmda = Button("OK", actionPerformed=self.close, font=f)
わずかこれだけ。これは簡単ですし、とても分かりやすいですね。

さて上の例はJava AWTとアプレットで書きましたが、 全く同様にして、軽量コンポーネントのSwingクラスも使うことができます。

import java
from java.awt import Dimension, Font
from javax.swing import *

class HelloWidget(JPanel):

    def __init__(self):
        f = Font("Helvetica", 0, 14)
        la = JLabel("ハローワールド")
        cmda = JButton("OK", actionPerformed=self.close, font=f)
        self.add(la)
        self.add(cmda)

    def close(self, event):
        java.lang.System.exit(0)

f = JFrame("Hello World Example")
f.setSize(Dimension(100, 50))
my = HelloWidget()
my.setPreferredSize(Dimension(100, 50))
f.getContentPane().add(my)
f.show()
# end.

はい。これはこれで、特に問題になるような点もないと思います。


というわけで

 いかがでしょう?Jythonは単にJavaで書かれたPythonというだけにとどまらず、 JavaクラスとPythonクラスの綺麗な融合に、 簡略記法の工夫を採り入れた高速プロトタイピングツールとしての便利さをも兼ね備えた、 まさに夢のようなツールです。この機会にぜひ、お求め下さい。 今なら4個セットにさらに2個ついてお値段据え置き価格で大奉仕!(収納箱モード)

セクションのサブメニューに戻る
(first uploaded 2001/06/05 last updated 2004/05/04)