JVMのための最新のPython

Pythonバージョン2(現在はEOL)の場合、JythonがPythonとJavaをインタフェースする事実上の手段です。Java統合を使用する既存のJythonコードのほとんどは、安定したJythonリリースに基づきます。ただし、これらはPython 2.xバージョンでのみ使用できます。一方、GraalPyはPython 3.xと互換性があり、以前の2.xバージョンのJythonとの完全な互換性はありません。

Python 2からPython 3にコードを移行するには、Pythonコミュニティからの公式ガイドに従います。JythonコードがPython 3と互換性を持つようになったら、このガイドに従って、GraalPyとJythonのその他の違いを解決します。

GraalPyのJava相互運用性に対するファーストクラスのサポートにより、PythonからJavaライブラリを非常に簡単に使用できます。Javaコードに対して、他のGraal言語(Truffleフレームワークに実装された言語)に対する一般的な相互運用性サポート以上のアフォーダンスがあります。

Jythonのすべての機能がGraalPyでサポートされているわけではありません。一部はサポートされていますが、ランタイム・パフォーマンスに悪影響を及ぼすため、デフォルトでは無効になっています。移行中に、コマンドライン・オプション--python.EmulateJythonを使用してこれらの機能を有効にできます。ただし、最適なパフォーマンスを実現するために、これらの機能を使用しないことをお薦めします。

Jythonスクリプトの移行

プレーンJythonスクリプトをJythonからGraalPyに移行するには、GraalPy JVMベースのランタイムを使用します。(詳細は、「GraalPyディストリビューション」を参照してください)。

Javaパッケージのインポート

JythonのJava統合には、デフォルトでGraalPyで有効になっている特定の機能があります。例:

>>> import java.awt as awt
>>> win = awt.Frame()
>>> win.setSize(200, 200)
>>> win.setTitle("Hello from Python!")
>>> win.getSize().toString()
'java.awt.Dimension[width=200,height=200]'
>>> win.show()

この例は、JythonとGraalPyの両方で実行すると同じ結果になります。ただし、この例をGraalPyで実行する場合、javaネームスペース内のパッケージのみを直接インポートできます。javaネームスペース外のパッケージからクラスをインポートするには、--python.EmulateJythonオプションを使用します。

ノート: モジュール化されたアプリケーションにGraalPyを埋め込む場合、JSR 376に従って必要なモジュールのエクスポートを追加する必要がある場合があります。

また、すべての状況でJavaパッケージをPythonモジュールとしてインポートできるわけではありません。たとえば、次のものは機能します:

import java.lang as lang

しかし、次のものは機能しません:

import javax.swing as swing
from javax.swing import *

かわりに、いずれかのクラスを直接インポートします:

import javax.swing.Window as Window

基本的なオブジェクトの使用方法

Javaオブジェクトおよびクラスの構築と操作は、従来のPython構文を使用して実現されます。Javaオブジェクトのメソッドは、Pythonメソッドと同様に、(インスタンスにバインドされた)ファースト・クラス・オブジェクトとして取得および参照することもできます。たとえば:

>>> from java.util import Random
>>> rg = Random(99)
>>> rg.nextInt()
1491444859
>>> boundNextInt = rg.nextInt
>>> boundNextInt()
1672896916

Java型とPython型: 自動変換

メソッドのオーバーロードは、ベストエフォート方式でPython引数を使用可能なパラメータ型と照合することによって解決されます。この方法は、データを変換するときにも使用されます。ここでの目標は、Pythonからできるかぎり円滑にJavaを使用できるようにすることです。GraalPyで使用される照合方法はJythonに似ていますが、GraalPyではより動的な照合方法が使用され、intまたはfloatをエミュレートするPython型も適切なJava型に変換されます。これにより、たとえば、Pandasフレームをdouble[][]として使用したり、NumPy配列要素をint[]として使用できます(これらの要素がそれらのJavaプリミティブ型に適合する場合)。

Java型 Python型
null None
boolean bool
byteshortintlong int__int__メソッドを持つ任意のオブジェクト
float float__float__メソッドを持つ任意のオブジェクト
char 長さ1のstr
java.lang.String str
byte[] bytesbytearray、適切な型のみを含むラップされたJava arrayおよびPythonリスト
Java配列 適切な型のみを含むラップされたJava arrayまたはPython list
Javaオブジェクト 適切な型のラップされたJavaオブジェクト
java.lang.Object 任意のオブジェクト

特別なJythonモジュール: jarray

GraalPyは、互換性のためにjarrayモジュール(プリミティブJava配列を作成する)を実装します。たとえば:

>>> import jarray
>>> jarray.array([1,2,3], 'i')

この使用方法は、次のように、java.type関数を使用して配列型を構築し、その配列に移入することと同じであることに注意してください:

>>> import java
>>> java.type("int[]")(10)

Java配列を作成するコードでPython型を使用することもできます。ただし、暗黙のうちに配列データのコピーが生成される可能性があり、Java配列を出力パラメータとして使用する場合、機能しない可能性があります:

>>> i = java.io.ByteArrayInputStream(b"foobar")
>>> buf = [0, 0, 0]
>>> i.read(buf) # buf is automatically converted to a byte[] array
3
>>> buf
[0, 0, 0] # the converted byte[] array is lost
>>> jbuf = java.type("byte[]")(3)
>>> i.read(jbuf)
3
>>> jbuf
[98, 97, 122]

Javaからの例外

Java例外を捕捉するには、--python.EmulateJythonオプションを使用します。

ノート: Java例外を捕捉すると、パフォーマンスが低下します。

たとえば:

>>> import java
>>> v = java.util.Vector()
>>> try:
...    x = v.elementAt(7)
... except java.lang.ArrayIndexOutOfBoundsException as e:
...    print(e.getMessage())
...
7 >= 0

Javaコレクション

Javaからの継承

Javaクラスからの継承(またはJavaインタフェースの実装)はサポートされますが、Jythonとは構文の違いがいくつかあります。Javaクラスから継承する(またはJavaインタフェースを実装する)クラスを作成するには、従来のPython class文を使用します。宣言されたメソッドは、名前が一致したときにスーパークラス(インタフェース)メソッドをオーバーライド(実装)します。スーパークラス・メソッドをコールするには、特別な属性self.__super__を使用します。作成されたオブジェクトはPythonオブジェクトのようには動作しませんが、かわりに外部Javaオブジェクトと同じように動作します。Pythonレベルのメンバーには、そのthis属性を使用してアクセスできます。たとえば:

import atexit
from java.util.logging import Logger, Handler


class MyHandler(Handler):
    def __init__(self):
        self.logged = []

    def publish(self, record):
        self.logged.append(record)


logger = Logger.getLogger("mylog")
logger.setUseParentHandlers(False)
handler = MyHandler()
logger.addHandler(handler)
# Make sure the handler is not used after the Python context has been closed
atexit.register(lambda: logger.removeHandler(handler))

logger.info("Hi")
logger.warning("Bye")

# The python attributes/methods of the object are accessed through 'this' attribute
for record in handler.this.logged:
    print(f'Python captured message "{record.getMessage()}" at level {record.getLevel().getName()}')

JavaへのPythonの埋込み

Jythonを使用する別の方法は、それをJavaアプリケーションに埋め込むことでした。このような埋込みには2つのオプションがありました。

  1. Jythonが提供するPythonInterpreterオブジェクトを使用します。JavaコードにはJython内部クラスへの参照があるため、この方法でJythonを使用する既存のコードは(Maven構成などの)Jythonパッケージに直接依存します。これらのクラスはGraalVMに存在せず、同等のクラスは公開されません。この使用状況から移行するには、GraalVM SDKに切り替えます。このSDKを使用すると、Python固有のAPIは公開されず、すべてがGraalVM APIを介して実現され、Pythonランタイムの最大構成が可能になります。設定の準備については、スタート・ガイドのドキュメントを参照してください。

  2. javax.scriptパッケージのクラス、特にScriptEngineクラスを使用して、JSR 223を介してJavaにJythonを埋め込みます。ScriptEngine APIはGraalPyのオプションおよび機能に完全には適合しないため、この方法はお薦めしません。ただし、既存のコードを移行するには、プロジェクトにインライン化できるScriptEngine実装の例を提供します。詳細は、埋込みのリファレンス・マニュアルを参照してください。