Cara Memanggil Python Dari Java

1. Ikhtisar

Python adalah bahasa pemrograman yang semakin populer, terutama di komunitas ilmiah karena banyaknya variasi paket numerik dan statistik. Oleh karena itu, bukan suatu persyaratan yang tidak biasa untuk dapat memanggil kode Python dari aplikasi Java kita.

Dalam tutorial ini, kita akan melihat beberapa cara paling umum untuk memanggil kode Python dari Java.

2. Skrip Python Sederhana

Sepanjang tutorial ini, kita akan menggunakan skrip Python yang sangat sederhana yang akan kita definisikan dalam file khusus bernama hello.py :

print("Hello Baeldung Readers!!")

Dengan asumsi kita memiliki instalasi Python yang berfungsi, ketika kita menjalankan skrip kita, kita akan melihat pesan tercetak:

$ python hello.py Hello Baeldung Readers!!

3. Inti Java

Di bagian ini, kita akan melihat dua opsi berbeda yang dapat kita gunakan untuk menjalankan skrip Python kita menggunakan inti Java.

3.1. Menggunakan ProcessBuilder

Pertama-tama mari kita lihat bagaimana kita dapat menggunakan ProcessBuilder API untuk membuat proses sistem operasi asli untuk meluncurkan python dan menjalankan skrip sederhana kita:

@Test public void givenPythonScript_whenPythonProcessInvoked_thenSuccess() throws Exception { ProcessBuilder processBuilder = new ProcessBuilder("python", resolvePythonScriptPath("hello.py")); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); List results = readProcessOutput(process.getInputStream()); assertThat("Results should not be empty", results, is(not(empty()))); assertThat("Results should contain output of script: ", results, hasItem( containsString("Hello Baeldung Readers!!"))); int exitCode = process.waitFor(); assertEquals("No errors should be detected", 0, exitCode); }

Dalam contoh pertama ini, kami menjalankan perintah python dengan satu argumen yang merupakan jalur absolut ke skrip hello.py kami . Kami dapat menemukannya di folder test / resources kami .

Untuk meringkas, kami membuat objek ProcessBuilder kami yang meneruskan perintah dan nilai argumen ke konstruktor. Penting juga untuk menyebutkan panggilan ke redirectErrorStream (true). Jika terjadi kesalahan, keluaran kesalahan akan digabungkan dengan keluaran standar.

Ini berguna karena artinya kita bisa membaca pesan kesalahan apa pun dari output terkait saat kita memanggil metode getInputStream () pada objek Proses . Jika kita tidak menyetel properti ini ke true , maka kita perlu membaca keluaran dari dua aliran terpisah, menggunakan metode getInputStream () dan getErrorStream () .

Sekarang, kita memulai proses menggunakan metode start () untuk mendapatkan objek Proses . Kemudian kami membaca output proses dan memverifikasi isinya adalah apa yang kami harapkan.

Seperti yang disebutkan sebelumnya, kami telah membuat asumsi bahwa perintah python tersedia melalui variabel PATH .

3.2. Bekerja Dengan Mesin Scripting JSR-223

JSR-223, yang pertama kali diperkenalkan di Java 6, mendefinisikan satu set API skrip yang menyediakan fungsionalitas skrip dasar. Metode ini menyediakan mekanisme untuk mengeksekusi skrip dan untuk berbagi nilai antara Java dan bahasa skrip. Tujuan utama dari standar ini adalah untuk mencoba membawa keseragaman dalam interoperating dengan bahasa scripting yang berbeda dari Java.

Kita dapat menggunakan arsitektur mesin skrip yang dapat dicolokkan untuk bahasa dinamis apa pun asalkan memiliki implementasi JVM, tentu saja. Jython adalah implementasi platform Java dari Python yang berjalan di JVM.

Dengan asumsi kita memiliki Jython di CLASSPATH , kerangka kerja akan secara otomatis menemukan bahwa kita memiliki kemungkinan untuk menggunakan mesin skrip ini dan memungkinkan kita untuk meminta mesin skrip Python secara langsung.

Karena Jython tersedia dari Maven Central, kami dapat memasukkannya ke dalam pom.xml kami :

 org.python jython 2.7.2 

Selain itu, juga dapat diunduh dan diinstal secara langsung.

Mari daftar semua mesin skrip yang kami miliki untuk kami:

ScriptEngineManagerUtils.listEngines();

Jika kami memiliki kemungkinan untuk menggunakan Jython, kami akan melihat mesin skrip yang sesuai ditampilkan:

... Engine name: jython Version: 2.7.2 Language: python Short Names: python jython 

Sekarang kita tahu kita dapat menggunakan mesin skrip Jython, mari kita lanjutkan dan lihat bagaimana memanggil skrip hello.py kita :

@Test public void givenPythonScriptEngineIsAvailable_whenScriptInvoked_thenOutputDisplayed() throws Exception { StringWriter writer = new StringWriter(); ScriptContext context = new SimpleScriptContext(); context.setWriter(writer); ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("python"); engine.eval(new FileReader(resolvePythonScriptPath("hello.py")), context); assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", writer.toString().trim()); }

Seperti yang bisa kita lihat, bekerja dengan API ini cukup mudah. Pertama, kita mulai dengan menyiapkan ScriptContext yang berisi StringWriter . Ini akan digunakan untuk menyimpan keluaran dari skrip yang ingin kita panggil.

Kami kemudian menggunakan metode getEngineByName dari kelas ScriptEngineManager untuk mencari dan membuat ScriptEngine untuk nama pendek yang diberikan . Dalam kasus kami, kami dapat mengirimkan python atau jython yang merupakan dua nama pendek yang terkait dengan mesin ini.

Seperti sebelumnya, langkah terakhir adalah mendapatkan output dari skrip kami dan memeriksanya sesuai dengan yang kami harapkan.

4. Jython

Melanjutkan dengan Jython, kami juga memiliki kemungkinan untuk menyematkan kode Python langsung ke kode Java kami. Kita bisa melakukan ini menggunakan kelas PythonInterpretor :

@Test public void givenPythonInterpreter_whenPrintExecuted_thenOutputDisplayed() { try (PythonInterpreter pyInterp = new PythonInterpreter()) { StringWriter output = new StringWriter(); pyInterp.setOut(output); pyInterp.exec("print('Hello Baeldung Readers!!')"); assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", output.toString() .trim()); } }

Menggunakan kelas PythonInterpreter memungkinkan kita untuk mengeksekusi string kode sumber Python melalui metode exec . Seperti sebelumnya kita menggunakan StringWriter untuk menangkap keluaran dari eksekusi ini.

Sekarang mari kita lihat contoh di mana kita menjumlahkan dua angka:

@Test public void givenPythonInterpreter_whenNumbersAdded_thenOutputDisplayed() { try (PythonInterpreter pyInterp = new PythonInterpreter()) { pyInterp.exec("x = 10+10"); PyObject x = pyInterp.get("x"); assertEquals("x: ", 20, x.asInt()); } }

Dalam contoh ini kita melihat bagaimana kita bisa menggunakan metode get , untuk mengakses nilai variabel.

Dalam contoh Jython terakhir kami, kami akan melihat apa yang terjadi ketika kesalahan terjadi:

try (PythonInterpreter pyInterp = new PythonInterpreter()) { pyInterp.exec("import syds"); }

When we run this code a PyException is thrown and we'll see the same error as if we were working with native Python:

Traceback (most recent call last): File "", line 1, in  ImportError: No module named syds

A few points we should note:

  • As PythonIntepreter implements AutoCloseable, it's good practice to use try-with-resources when working with this class
  • The PythonInterpreter class name does not imply that our Python code is interpreted. Python programs in Jython are run by the JVM and therefore compiled to Java bytecode before execution
  • Although Jython is the Python implementation for Java, it may not contain all the same sub-packages as native Python

5. Apache Commons Exec

Another third-party library that we could consider using is Apache Common Exec which attempts to overcome some of the shortcomings of the Java Process API.

The commons-exec artifact is available from Maven Central:

 org.apache.commons commons-exec 1.3 

Now let's how we can use this library:

@Test public void givenPythonScript_whenPythonProcessExecuted_thenSuccess() throws ExecuteException, IOException { String line = "python " + resolvePythonScriptPath("hello.py"); CommandLine cmdLine = CommandLine.parse(line); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream); DefaultExecutor executor = new DefaultExecutor(); executor.setStreamHandler(streamHandler); int exitCode = executor.execute(cmdLine); assertEquals("No errors should be detected", 0, exitCode); assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", outputStream.toString() .trim()); }

This example is not too dissimilar to our first example using ProcessBuilder. We create a CommandLine object for our given command. Next, we set up a stream handler to use for capturing the output from our process before executing our command.

To summarize, the main philosophy behind this library is to offer a process execution package aimed at supporting a wide range of operating systems through a consistent API.

6. Utilizing HTTP for Interoperability

Let's take a step back for a moment and instead of trying to invoke Python directly consider using a well-established protocol like HTTP as an abstraction layer between the two different languages.

In actual fact Python ships with a simple built-in HTTP server which we can use for sharing content or files over HTTP:

python -m http.server 9000

If we now go to //localhost:9000, we'll see the contents listed for the directory where we launched the previous command.

Some other popular frameworks we could consider using for creating more robust Python-based web services or applications are Flask and Django.

Once we have an endpoint we can access, we can use any one of several Java HTTP libraries to invoke our Python web service/application implementation.

7. Conclusion

Dalam tutorial ini, kita telah belajar tentang beberapa teknologi paling populer untuk memanggil kode Python dari Java.

Seperti biasa, kode sumber lengkap artikel tersedia di GitHub.