極少數時候,我們會碰到類似這樣的問題:與A同學合作寫代碼, A同學隻會寫Python,不熟悉Java ,而你隻會寫Java不擅長Python,并且發現難以用Java來重寫對方的代碼,這時,就不得不想方設法“調用對方的代碼”。
下面,我通過一些簡單的小例子來說明:如何在Java中調用Python代碼。
什麼是Jython?
Jython(原JPython),可以了解為一個由Java語言編寫的Python解釋器。
要使用Jython,隻需要将Jython-x.x.x.jar檔案置于classpath中即可 --> 官網下載下傳
當然,通過Maven導入也OK,如下:
org.python
jython-standalone
2.7.0
一個HelloPython程式
importorg.python.util.PythonInterpreter;public classHelloPython {public static voidmain(String[] args) {
PythonInterpreter interpreter= newPythonInterpreter();
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代碼:
defhello():return 'Hello'
在Java代碼中調用這個Python函數:
importorg.python.core.PyFunction;importorg.python.core.PyObject;importorg.python.util.PythonInterpreter;public classHelloPython {public static voidmain(String[] args) {
PythonInterpreter interpreter= newPythonInterpreter();
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類型諸如:PyString、
在本地環境中調用Python腳本
例子程式(與舍友合作寫的一個手寫識别程式,可識别很粗的數字,JAVA寫界面,Python識别):
import java.io.*;classPyCaller {private static final String DATA_SWAP = "temp.txt";private static final String PY_URL = System.getProperty("user.dir") + "\\test.py";public static voidwriteImagePath(String path) {
PrintWriter pw= null;try{
pw= new PrintWriter(new FileWriter(newFile(DATA_SWAP)));
}catch(IOException e) {
e.printStackTrace();
}
pw.print(path);
pw.close();
}public staticString readAnswer() {
BufferedReader br;
String answer= null;try{
br= new BufferedReader(new FileReader(newFile(DATA_SWAP)));
answer=br.readLine();
}catch(FileNotFoundException e) {
e.printStackTrace();
}catch(IOException e) {
e.printStackTrace();
}returnanswer;
}public static voidexecPy() {
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) throwsIOException, InterruptedException {
writeImagePath("D:\\labs\\mytest\\test.jpg");
execPy();
System.out.println(readAnswer());
}
}
運作流程:Java Swing 界面接收使用者輸入 --> Java 将使用者輸入寫到本地檔案中 --> Java 調用本地 Python 腳本 --> Python 從本地檔案拿到使用者輸入 --> Python 處理使用者輸入得到最終結果 --> Python 把最終結果寫到本地檔案 --> Java 對 Python 腳本的調用結束 --> Java 從本地檔案中取出最終結果 --> Java 把最終結果傳回給使用者