JavaでPythonを実行するJythonで、Javaにある値をPythonに渡す方法と、Pythonにある値をJavaに持ってくる方法です。
Pythonの型で値を渡すためのPyObjectの生成方法も紹介します。
PythonInterpreterでの受け渡し
JavaからPythonに値を渡す
public static void main(String[] args) {
StringWriter writer = new StringWriter();
try(PythonInterpreter pyInterp = new PythonInterpreter()) {
pyInterp.setOut(writer);
PyInteger num = new PyInteger(5);
pyInterp.set("num", num);
pyInterp.exec("print(num)");
pyInterp.exec("print(type(num))");
System.out.println(writer);
}
}
5
<type 'int'>
Pythonで使う変数名と、インスタンスを表すPyObjectまたはそのサブクラスを、PythonInterpreterにset
することで変数を定義できます。
PythonからJavaに値を持ってくる
public static void main(String[] args) {
try(PythonInterpreter pyInterp = new PythonInterpreter()) {
pyInterp.exec("num = 10");
PyObject pyObj = pyInterp.get("num");
Object o = pyObj.__tojava__(Integer.class);
System.out.println(o);
System.out.println(o.getClass());
}
}
10
class java.lang.Integer
get
メソッドでPyObjectを取得できます。
PyObjectには__tojava__
メソッドがあり、引数に入れた型への変換に対応していれば、その型へ変換して返します。Object型はどのクラスも対応しているので、Object.class
を引数に入れればとりあえず変換してくれます。
対応していない場合はError
を表すPyObjectのサブクラスが返ってきます。
Object o = pyObj.__tojava__(Date.class);
Error
class org.python.core.PySingleton
ScriptEngineでの受け渡し
public static void main(String[] args) throws ScriptException {
String script = "from decimal import Decimal\n" +
"y = num + Decimal(10)";
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("python");
SimpleBindings bindings = new SimpleBindings();
PyInteger num = new PyInteger(5);
bindings.put("num", num);
scriptEngine.eval(script, bindings);
Object y = bindings.get("y");
System.out.println(y);
System.out.println(y.getClass());
}
15
class java.math.BigDecimal
ScriptEngineで実行するときも同様に、SimpleBindingsを通して値を受け渡しできます。
スクリプト実行後のSimpleBindingsには、PyObjectの__tojava__(Object.class)
で変換した値が格納されています。
PyObjectの生成
JavaからPythonに値を渡すときに、PyObjectでなくても直接Javaの値を渡すことができます。
public static void main(String[] args) {
StringWriter writer = new StringWriter();
try (PythonInterpreter pyInterp = new PythonInterpreter()) {
pyInterp.setOut(writer);
pyInterp.set("num", 0.5);
pyInterp.set("dt", new Date()); // java.util.Date型を渡す
pyInterp.exec("print(num)");
pyInterp.exec("print(type(num))");
pyInterp.exec("print(dt)");
pyInterp.exec("print(type(dt))");
System.out.println(writer);
}
}
0.5
<type 'float'>
Mon Jul 31 01:01:41 JST 2023
<type 'java.util.Date'>
しかし、Pythonの型に変換されるのは、数値や文字列などの一部の基本的なJavaの型のみです。サポートされていない型は、Javaの型のままPythonに渡されます。
そのため、Pythonの型として値を渡したい場合は、PyObjectを生成して値を渡す必要があります。
モジュールのクラスのインスタンスを生成する
数値や文字列のPyObjectはjavaで実装されているため、new PyInteger
のようにPyObjectを直接生成することが可能でした。
.pyファイルで実装されているモジュールのクラスを利用したい場合は、クラスを表すPyObjetからコンストラクタやファクトリメソッドを実行してインスタンスのPyObjectを生成します。
public static void main(String[] args) {
StringWriter writer = new StringWriter();
try(PythonInterpreter pyInterp = new PythonInterpreter()) {
pyInterp.setOut(writer);
PyObject datetimeModule = __builtin__.__import__("datetime");
PyObject datetimeClass = datetimeModule.__getattr__("datetime");
PyObject strptimeMethod = datetimeClass.__getattr__("strptime");
PyObject dt = strptimeMethod.__call__(new PyObject[]{
new PyString("2023/07/31 12:34:56"),
new PyString("%Y/%m/%d %H:%M:%S")
});
pyInterp.set("dt", dt);
pyInterp.exec("print(dt)");
pyInterp.exec("print(type(dt))");
System.out.println(writer);
}
}
2023-07-31 12:34:56
<class 'datetime.datetime'>
strptime
メソッドを表すPyObjectに、必要な引数をPyObject[]で渡して__call__
を呼ぶことで、strptimeを実行しています。これでdatetimeクラスのインスタンスのPyObjectが生成されます。
Pyクラスから生成する
PyInteger num = Py.newInteger(5);
PyObject decimal = Py.newDecimal("123.456");
PyクラスはJythonの内部で使う基本的な機能を提供するユーティリティクラスで、newから始まるメソッドには、インスタンスを表すPyObjectを生成する機能が実装されています。
PyObjectAdapterで生成する
数値や文字列などの一部のJavaの型が自動でPythonの型に変換されていたのは、PyObjectAdapterで対応するPyObjectを生成していたためです。
今回はサブクラスであるClassAdapterを実装し、Dateクラスからdatetimeクラスへの変換を追加してみます。
static class DateAdapter extends ClassAdapter {
DateAdapter() {
super(Date.class);
}
@Override
public PyObject adapt(Object o) {
Date date = (Date) o;
return Py.newDatetime(new Timestamp(date.getTime()));
}
}
public static void main(String[] args) {
StringWriter writer = new StringWriter();
try (PythonInterpreter pyInterp = new PythonInterpreter()) {
pyInterp.setOut(writer);
Py.getAdapter().add(new DateAdapter()); // アダプターを追加する
pyInterp.set("dt", new Date()); // java.util.Date型を渡す
pyInterp.exec("print(dt)");
pyInterp.exec("print(type(dt))");
System.out.println(writer);
}
}
2023-07-31 01:25:17.395000
<class 'datetime.datetime'>
Date型を直接渡しましたが、型が<type 'java.util.Date'>
ではなく<class 'datetime.datetime'>
になりました。