在Java中調用Python代碼


極少數時候,我們會碰到類似這樣的問題:與A同學合作寫代碼, A同學只會寫Python,不熟悉Java ,而你只會寫Java不擅長Python,並且發現難以用Java來重寫對方的代碼,這時,就不得不想方設法“調用對方的代碼”。

下面,我通過一些簡單的小例子來說明:如何在Java中調用Python代碼。

什么是Jython? 

Jython(原JPython),可以理解為一個由Java語言編寫的Python解釋器。

要使用Jython,只需要將Jython-x.x.x.jar文件置於classpath中即可 --> 官網下載

當然,通過Maven導入也OK,如下:

<dependency>
    <groupId>org.python</groupId>
    <artifactId>jython-standalone</artifactId>
    <version>2.7.0</version>
</dependency>

 

一個HelloPython程序

import org.python.util.PythonInterpreter; public class HelloPython { public static void main(String[] args) { PythonInterpreter interpreter = new PythonInterpreter(); interpreter.exec("print('hello')"); } }

什么是PythonInterpreter呢?它的中文意思即“Python解釋器”。我們知道Python程序都是由解釋器執行的,上面的代碼在JVM中創建一個“Python解釋器”對象,用於模擬Python解釋器的行為,並通過exec("Python語句") 直接在JVM中執行Python代碼,代碼的輸出結果為:hello。該程序運行速度相較正常的Java或者Python程序都要慢那么一點。

 

在JVM中執行Python腳本

interpreter.execfile("D:/labs/mytest/hello.py");  

如上,將exec改為execfile就可以了。需要注意的是,這個 .py文件不能含有第三方模塊,因為這個“Python腳本”說到底仍是在JVM環境下執行的(而非依賴於本地計算機環境),如果 .py 程序中包含有第三方模塊(例如 NumPy)將會在編譯期報錯:java ImportError: No module named xxx

 

在JVM中調用Python編寫的函數

先寫一個hello.py的Python代碼:

def hello(): return 'Hello'

在Java代碼中調用這個Python函數:

import org.python.core.PyFunction; import org.python.core.PyObject; import org.python.util.PythonInterpreter; public class HelloPython { public static void main(String[] args) { PythonInterpreter interpreter = new PythonInterpreter(); interpreter.execfile("D:/labs/hello.py"); PyFunction pyFunction = interpreter.get("hello", PyFunction.class); // 第一個參數為期望獲得的函數(變量)的名字,第二個參數為期望返回的對象類型
        PyObject pyObject = pyFunction.__call__(); // 調用函數
 System.out.println(pyObject); } }

上面的代碼執行結果為:Hello

可以發現:即便只是調用一個函數,也必須先加載整個 .py文件,之后才能通過Jython包中所定義的類獲取、調用這個函數。

如果Python函數需要參數,必須先將Java代碼中的參數轉化為對應的“Python類型”(姑且可以稱作 Jython 類型 (●'◡'●),例如:

__call__(new PyInteger(a), new PyInteger(b))

a,b的類型均為Java中的int型,還有一些Jython類型諸如:PyStringPyList,詳細信息可以參考官方的API文檔。

 

在本地環境中調用Python腳本

由於Jython運行過慢並且不支持第三方的Python模塊,也許通過Java執行一段終端命令來調用Python腳本才是實際會用的方式。以下為和舍友合作寫的“手寫識別”程序中的一個代碼片段(Python做識別,Java寫界面):

import java.io.*; class PyCaller { private static final String DATA_SWAP = "temp.txt"; private static final String PY_URL = System.getProperty("user.dir") + "\\test.py"; public static void writeImagePath(String path) { PrintWriter pw = null; try { pw = new PrintWriter(new FileWriter(new File(DATA_SWAP))); } catch (IOException e) { e.printStackTrace(); } pw.print(path); pw.close(); } public static String readAnswer() { BufferedReader br; String answer = null; try { br = new BufferedReader(new FileReader(new File(DATA_SWAP))); answer = br.readLine(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return answer; } public static void execPy() { Process proc = null; try { proc = Runtime.getRuntime().exec("python " + PY_URL); proc.waitFor(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } // 測試碼
    public static void main(String[] args) throws IOException, InterruptedException {  writeImagePath("D:\\labs\\mytest\\test.jpg"); execPy(); System.out.println(readAnswer()); } }

運行流程:Java Swing 界面接收用戶輸入 --> Java 將用戶輸入寫到本地文件中 --> Java 調用本地 Python 腳本 --> Python 從本地文件拿到用戶輸入 --> Python 處理用戶輸入得到最終結果 --> Python 把最終結果寫到本地文件 --> Java 對 Python 腳本的調用結束 --> Java 從本地文件中取出最終結果 --> Java 把最終結果返回給用戶

完整代碼鏈接:http://pan.baidu.com/s/1sl4l68H 

 


后續補。這塊內容已經寫了好多年了。但是發現居然有出乎意料的瀏覽量。感覺有很多不妥之處。恐怕會對新手產生誤導(萬一 。。。),所以特意補上一段。上面的內容是基於大學初期的一次課設作業,當時好像是必須強行進行合作寫代碼,完成一個什么任務。。現在好多年過去了,我至今沒有發現什么場景是真的需要Jython。而用Java執行終端命令來調Python現在看來也是非常不妥的。總之,實際開發中1000%有更漂亮的方法。上述方法可能僅適合寫一些玩具式程序,甚至寫玩具式程序都感覺未免“臟”了一些。。。所以, 僅  供 參 考。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM