Jython移行ガイド
Java統合を使用するほとんどのJythonコードは、安定したJythonリリースに基づくものと考えられ、これらはPython 2.xバージョンでのみ機能します。一方、GraalVMのPythonランタイムはPython 3.xのみをターゲットにしています。GraalVMは、このような以前の2.xバージョンのJythonとの完全な互換性を備えていません。したがって、すべてのコードをPython 3に移行するには、重要な移行ステップを実行する必要があります。
Jython固有の機能については、このドキュメントに従ってGraalVMのPythonランタイムへの移行について学習してください。
Jythonの一部の機能はランタイム・パフォーマンスに悪影響を及ぼすため、デフォルトでは無効になっています。移行を容易にするために、GraalVMでコマンドライン・フラグ--python.EmulateJython
を使用して一部の機能を有効にできます。
Javaパッケージのインポート
JythonのJava統合には、GraalVMのPythonランタイムでデフォルトで有効になっている特定の機能があります。例:
>>> 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()
この例は、GraalVM上のJythonとPythonの両方でまったく同じように機能します。ただし、GraalVMでは、java
ネームスペース内のパッケージのみを直接インポートできます。java
ネームスペース外のパッケージからもクラスをインポートするには、--python.EmulateJython
オプションを有効にする必要があります。
また、JavaパッケージをPythonモジュールとしてインポートすることは、非常に特殊な状況でのみサポートされます。たとえば、次のものは機能します:
import java.lang as lang
次のものは機能しません:
import javax.swing as swing
from javax.swing import *
かわりに、目的のクラスのいずれかを直接インポートする必要があります:
import javax.swing.Window as Window
基本的なオブジェクトの使用方法
Javaオブジェクトおよびクラスの構築と操作は、自然なPython構文を使用して行われます。また、Pythonメソッドと同様に、Javaオブジェクトのメソッドを(インスタンスにバインドされた)最初のクラス・オブジェクトとして取得したり、渡すこともできます:
>>> from java.util import Random
>>> rg = Random(99)
>>> rg.nextInt()
1491444859
>>> boundNextInt = rg.nextInt
>>> boundNextInt()
1672896916
Java型とPython型: 自動変換
メソッドのオーバーロードは、ベストエフォート方式でPython引数を使用可能なパラメータ型と照合することによって解決されます。このことは、データ変換中にも行われます。ここでの目標は、Pythonからできるかぎり円滑にJavaを使用できるようにすることです。ここで許可されている照合はJythonに似ていますが、GraalVMのPythonランタイムではより動的な照合方法が使用され、int
またはfloat
をエミュレートするPython型も適切なJava型に変換されます。これにより、たとえば、Pandasフレームをdouble[][]
として使用したり、NumPy配列要素をint[]
として使用できます(これらの要素がそれらのJavaプリミティブ型に適合する場合)。
Java型 | Python型 |
---|---|
null | なし |
boolean | bool |
byte、short、int、long | int、__int__ メソッドを持つ任意のオブジェクト |
float | float、__float__ メソッドを持つ任意のオブジェクト |
char | 長さ1のstr |
java.lang.String | str |
byte[] | bytes、bytearray、適切な型のみを含むラップされたJava配列またはPythonリスト |
Java配列 | 適切な型のみを含むラップされたJava配列またはPythonリスト |
Javaオブジェクト | 適切な型のラップされたJavaオブジェクト |
java.lang.Object | 任意のオブジェクト |
特殊なJythonモジュール
プリミティブJava配列の作成に使用されるjarray
モジュールは、互換性のためにサポートされています。
>>> 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 got lost
>>> jbuf = java.type("byte[]")(3)
>>> i.read(jbuf)
3
>>> jbuf
[98, 97, 122]
jarray
以外のモジュールはサポートされていません。
Javaからの例外
すべての種類のJava例外を捕捉することはパフォーマンス・ペナルティを伴い、--python.EmulateJython
オプションでのみ有効になります。
>>> 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クラスからの継承またはインタフェースの実装はサポートされますが、Jythonとは構文の違いがいくつかあります。Javaクラスから継承するクラスは、通常の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 have to be 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アプリケーションに埋め込むことです。前述のGraalVMのPythonランタイムが既存のJythonコードとの互換性をある程度備えていた場合、このケースでは何も提供されません。JavaコードにはPythonInterpreter
などのJython内部クラスへの参照があるため、Jythonを使用する既存のコードは(Maven構成などの) Jythonパッケージに直接依存します。
GraalVMのPythonランタイムについては、GraalVM SDK以外に対する依存性は必要ありません。公開されている、Pythonに固有のAPIはなく、すべてがGraalVM APIを介して実行されます。
Python言語がインストールされたGraalVMでアプリケーションが実行されているかぎり、Pythonをプログラムに埋め込むことができる点に注意することが重要です。詳細は、『Embed Languages』ガイドを参照してください。