Cara Memanggil Python Dari Java

1. Gambaran keseluruhan

Python adalah bahasa pengaturcaraan yang semakin popular, terutama dalam komuniti saintifik kerana pelbagai pakej berangka dan statistiknya. Oleh itu, bukan syarat yang luar biasa untuk menggunakan kod Python dari aplikasi Java kami.

Dalam tutorial ini, kita akan melihat beberapa cara yang paling biasa untuk memanggil kod Python dari Java.

2. Skrip Python Mudah

Sepanjang tutorial ini, kami akan menggunakan skrip Python yang sangat mudah yang akan kami tetapkan dalam fail khusus yang disebut hello.py :

print("Hello Baeldung Readers!!")

Dengan andaian kita mempunyai pemasangan Python yang berfungsi, ketika kita menjalankan skrip kita, kita akan melihat mesej dicetak:

$ python hello.py Hello Baeldung Readers!!

3. Java Teras

Pada bahagian ini, kita akan melihat dua pilihan berbeza yang dapat kita gunakan untuk menggunakan skrip Python kita menggunakan Java inti.

3.1. Menggunakan ProcessBuilder

Mari kita lihat bagaimana kita dapat menggunakan ProcessBuilder API untuk membuat proses sistem operasi asli untuk melancarkan python dan melaksanakan skrip mudah kami:

@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 jalan mutlak ke skrip hello.py kami . Kami boleh mencarinya di folder ujian / sumber kami .

Untuk meringkaskan, kami membuat objek ProcessBuilder kami menyampaikan nilai perintah dan argumen kepada pembina. Penting juga untuk menyebut panggilan untuk mengalihkanErrorStream (benar). Sekiranya terdapat kesalahan, output kesalahan akan digabungkan dengan output standard.

Ini berguna kerana ini bermaksud kita dapat membaca sebarang mesej ralat dari output yang sesuai ketika kita memanggil kaedah getInputStream () pada objek Proses . Sekiranya kita tidak menetapkan sifat ini menjadi benar , maka kita perlu membaca output dari dua aliran berasingan, menggunakan kaedah getInputStream () dan getErrorStream () .

Sekarang, kita memulakan proses menggunakan kaedah start () untuk mendapatkan objek Proses . Kemudian kami membaca output proses dan mengesahkan kandungannya seperti yang kami harapkan.

Seperti yang telah disebutkan sebelumnya, kami membuat andaian bahawa perintah python tersedia melalui pemboleh ubah PATH .

3.2. Bekerja dengan Mesin Skrip JSR-223

JSR-223, yang pertama kali diperkenalkan di Java 6, mendefinisikan sekumpulan API skrip yang menyediakan fungsi skrip asas. Kaedah ini menyediakan mekanisme untuk melaksanakan skrip dan untuk berkongsi nilai antara Java dan bahasa skrip. Objektif utama standard ini adalah untuk mencoba membawa keseragaman untuk beroperasi dengan bahasa skrip yang berbeza dari Java.

Kita boleh menggunakan seni bina mesin skrip yang dapat dipasang untuk bahasa dinamis dengan syarat ia mempunyai implementasi JVM, tentu saja. Jython adalah pelaksanaan platform Python Java yang berjalan di JVM.

Dengan andaian bahawa kita memiliki Jython di CLASSPATH , kerangka kerja secara automatik akan mengetahui bahawa kita memiliki kemungkinan menggunakan mesin skrip ini dan memungkinkan kita untuk meminta mesin skrip Python secara langsung.

Oleh kerana Jython boleh didapati dari Maven Central, kami boleh memasukkannya ke dalam pom.xml kami :

 org.python jython 2.7.2 

Begitu juga, ia juga boleh dimuat turun dan dipasang secara langsung.

Mari senaraikan semua enjin skrip yang kami ada:

ScriptEngineManagerUtils.listEngines();

Sekiranya kita mempunyai kemungkinan menggunakan Jython, kita harus melihat enjin skrip yang sesuai dipaparkan:

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

Sekarang kita tahu bahawa kita boleh menggunakan mesin skrip Jython, mari maju dan lihat bagaimana memanggil skrip hello.py kami :

@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 kita lihat, cukup mudah untuk bekerja dengan API ini. Pertama, kita mulakan dengan menyediakan ScriptContext yang mengandungi StringWriter . Ini akan digunakan untuk menyimpan output dari skrip yang ingin kita gunakan.

Kami kemudian menggunakan kaedah getEngineByName dari kelas ScriptEngineManager untuk mencari dan membuat ScriptEngine untuk nama pendek yang diberikan . Dalam kes kita, kita boleh melewati python atau jython yang merupakan dua nama pendek yang berkaitan dengan enjin ini.

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

4. Jython

Melanjutkan dengan Jython, kami juga memiliki kemungkinan untuk memasukkan kode Python terus ke dalam kod Java kami. Kita boleh 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 membolehkan kita melaksanakan rentetan kod sumber Python melalui kaedah exec . Seperti sebelumnya kita menggunakan StringWriter untuk menangkap output dari pelaksanaan ini.

Sekarang mari kita lihat contoh di mana kita menambah dua nombor bersama:

@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 dapat menggunakan kaedah get , untuk mengakses nilai pemboleh ubah.

Dalam contoh Jython terakhir kami, kami akan melihat apa yang berlaku apabila ralat berlaku:

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, kami telah mengetahui beberapa teknologi yang paling popular untuk memanggil kod Python dari Java.

Seperti biasa, kod sumber penuh artikel terdapat di GitHub.