LoginSignup
1
2

JythonでPythonとJava間での値の受け渡し

Last updated at Posted at 2023-07-31

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'>になりました。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2