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 |
byte、short、int、long |
int、__int__メソッドを持つ任意のオブジェクト |
float |
float、__float__メソッドを持つ任意のオブジェクト |
char |
長さ1のstr |
java.lang.String |
str |
byte[] |
bytes、bytearray、適切な型のみを含むラップされた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.util.Collectionインタフェースを実装するJava配列およびコレクションには、[]構文を使用してアクセスできます。空のコレクションはブール変換ではfalseとみなされます。コレクションの長さは、len組込み関数によって公開されます。たとえば:>>> from java.util import ArrayList >>> l = ArrayList() >>> l.add("foo") True >>> l.add("baz") True >>> l[0] 'foo' >>> l[1] = "bar" >>> del l[1] >>> len(l) 1 >>> bool(l) True >>> del l[0] >>> bool(l) False -
java.lang.Iterableインタフェースを実装するJava反復可能オブジェクトは、forループまたはiter組込み関数を使用して反復できます。また、反復可能オブジェクトを予期するすべての組込み関数によって受け入れられます。たとえば:>>> [x for x in l] ['foo', 'bar'] >>> i = iter(l) >>> next(i) 'foo' >>> next(i) 'bar' >>> next(i) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> set(l) {'foo', 'bar'} -
イテレータも反復できます。たとえば:
>>> from java.util import ArrayList >>> l = ArrayList() >>> l.add("foo") True >>> i = l.iterator() # Calls the Java iterator methods >>> next(i) 'foo' -
java.util.Mapインタフェースを実装するマップされたコレクションには、[]表記法を使用してアクセスできます。空のマップはブール変換ではfalseとみなされます。マップを反復すると、dictと一致するキーが生成されます。たとえば:>>> from java.util import HashMap >>> m = HashMap() >>> m['foo'] = 5 >>> m['foo'] 5 >>> m['bar'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: bar >>> [k for k in m] ['foo'] >>> bool(m) True >>> del m['foo'] >>> bool(m) False
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つのオプションがありました。
-
Jythonが提供する
PythonInterpreterオブジェクトを使用します。JavaコードにはJython内部クラスへの参照があるため、この方法でJythonを使用する既存のコードは(Maven構成などの)Jythonパッケージに直接依存します。これらのクラスはGraalVMに存在せず、同等のクラスは公開されません。この使用状況から移行するには、GraalVM SDKに切り替えます。このSDKを使用すると、Python固有のAPIは公開されず、すべてがGraalVM APIを介して実現され、Pythonランタイムの最大構成が可能になります。設定の準備については、スタート・ガイドのドキュメントを参照してください。 -
javax.scriptパッケージのクラス、特にScriptEngineクラスを使用して、JSR 223を介してJavaにJythonを埋め込みます。ScriptEngineAPIはGraalPyのオプションおよび機能に完全には適合しないため、この方法はお薦めしません。ただし、既存のコードを移行するには、プロジェクトにインライン化できるScriptEngine実装の例を提供します。詳細は、埋込みのリファレンス・マニュアルを参照してください。