今回は py4j を使って Java の API を Python から利用してみる。
py4j のアーキテクチャはサーバ・クライアントモデルになっている。 つまり、まず Java の API を Python から叩けるように、Java でゲートウェイサーバとなるプログラムを書く。 そして、Python からはネットワーク経由でそのゲートウェイサーバにアクセスする。 これは、RPC (Remote Procedure Call) の考え方に近い。
使った環境は次の通り。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.5 BuildVersion: 18F132 $ python -V Python 3.7.3 $ java -version openjdk version "12.0.1" 2019-04-16 OpenJDK Runtime Environment (build 12.0.1+12) OpenJDK 64-Bit Server VM (build 12.0.1+12, mixed mode, sharing)
下準備
下準備として Java (JDK) と py4j をインストールしておく。
$ brew cask install java $ pip install py4j
ゲートウェイサーバを記述する
最初に、Python から利用したい Java の API に対してゲートウェイサーバを書く。
以下のサンプルコードでは HelloWorld クラスの API についてゲートウェイサーバを用意している。
基本的にはクラスのインスタンスを GatewayServer
クラスに渡してやるだけ。
提供されるのは割り算の機能を持った div()
メソッドになる。
import py4j.GatewayServer; public class HelloWorld { public int div(int a, int b) { // 割り算の機能を提供するメソッド return a / b; } public static void main(String[] args) { // GatewayServer 経由で機能を提供する HelloWorld application = new HelloWorld(); GatewayServer gateway = new GatewayServer(application); gateway.start(); System.out.println("Starting server..."); } }
続いて、上記を Java バイトコードにコンパイルする。 ただし、それには py4j の jar ファイルが必要になる。
jar ファイルは py4j のインストール先にある。 なので、まずは py4j のインストールされている場所を確認する。
$ python -c "import py4j; print(py4j.__path__)" ['/Users/amedama/.virtualenvs/py37/lib/python3.7/site-packages/py4j']
次のように share
ディレクトリ以下に jar ファイルがあった。
$ ls ~/.virtualenvs/py37/share/py4j py4j0.10.8.1.jar
この jar ファイルにクラスパスを通しながら、先ほどのプログラムをコンパイルする。
$ javac -cp ~/.virtualenvs/py37/share/py4j/py4j0.10.8.1.jar HelloWorld.java
次のように class
ファイルが完成すれば上手くいっている。
$ file HelloWorld.class
HelloWorld.class: compiled Java class data, version 56.0
コンパイルできたら、ゲートウェイサーバのプログラムを起動する。 この際にも、jar ファイルにクラスパスを通す必要がある。
$ java -cp ~/.virtualenvs/py37/share/py4j/py4j0.10.8.1.jar:. HelloWorld Starting server...
これで、デフォルトでは TCP:25333
で py4j のサービスが起動する。
$ lsof -i | grep 25333 java 10364 amedama 6u IPv6 0xbd776a50dc8d3c71 0t0 TCP localhost:25333 (LISTEN)
Python から利用する
これで準備ができたらので、Python から利用してみよう。
まずは Python のインタプリタを起動する。
$ python
py4j 経由で Java の API を呼び出すために JavaGateway
クラスのインスタンスを用意する。
>>> from py4j.java_gateway import JavaGateway >>> java_gateway = JavaGateway() >>> java_app = java_gateway.entry_point
あとは Java の API を呼び出すだけ。
>>> java_app.div(20, 10) 2
ちゃんと割り算ができている。
試しに Java のプログラム上で例外を発生させてみよう。
すると、次のように py4j.protocol.Py4JJavaError
例外となる。
例外の中には、Java 上で発生した例外の情報が入っている。
>>> java_app.div(1, 0) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/amedama/.virtualenvs/py37/lib/python3.7/site-packages/py4j/java_gateway.py", line 1286, in __call__ answer, self.gateway_client, self.target_id, self.name) File "/Users/amedama/.virtualenvs/py37/lib/python3.7/site-packages/py4j/protocol.py", line 328, in get_return_value format(target_id, ".", name), value) py4j.protocol.Py4JJavaError: An error occurred while calling t.div. : java.lang.ArithmeticException: / by zero at HelloWorld.div(HelloWorld.java:7) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at py4j.reflection.MethodInvoker.invoke(MethodInvoker.java:244) at py4j.reflection.ReflectionEngine.invoke(ReflectionEngine.java:357) at py4j.Gateway.invoke(Gateway.java:282) at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132) at py4j.commands.CallCommand.execute(CallCommand.java:79) at py4j.GatewayConnection.run(GatewayConnection.java:238) at java.base/java.lang.Thread.run(Thread.java:835)
いじょう。
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者: もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版
- この商品を含むブログ (1件) を見る