Java調(diào)用Python代碼的幾種方法小結
引言
Python語言有豐富的系統(tǒng)管理、數(shù)據(jù)處理、統(tǒng)計類軟件包,因此從java應用中調(diào)用Python代碼的需求很常見、實用。DataX 是阿里開源的一個異構數(shù)據(jù)源離線同步工具,致力于實現(xiàn)包括關系型數(shù)據(jù)庫(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各種異構數(shù)據(jù)源之間穩(wěn)定高效的數(shù)據(jù)同步功能。Datax也是通過Java調(diào)用Python腳本。本文介紹幾種方法從java調(diào)用Python代碼,從而最大化利用兩個語言的優(yōu)勢。
Java core
Java提供了有兩種方法,分別為ProcessBuilder API和 JSR-223 Scripting Engine。
使用ProcessBuilder
通過ProcessBuilder創(chuàng)建本地操作系統(tǒng)進程啟動python并執(zhí)行Python腳本, hello.py腳本簡單輸出“Hello Python!”。需要開發(fā)環(huán)境已經(jīng)安裝了python,并設置了環(huán)境變量。
@Test
public void givenPythonScript_whenPythonProcessInvoked_thenSuccess() throws Exception {
ProcessBuilder processBuilder = new ProcessBuilder("python", resolvePythonScriptPath("hello.py"));
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
List<String> 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 Python!")));
int exitCode = process.waitFor();
assertEquals("No errors should be detected", 0, exitCode);
}
private List<String> readProcessOutput(InputStream inputStream) throws IOException {
try (BufferedReader output = new BufferedReader(new InputStreamReader(inputStream))) {
return output.lines()
.collect(Collectors.toList());
}
}
private String resolvePythonScriptPath(String filename) {
File file = new File("src/test/resources/" + filename);
return file.getAbsolutePath();
}
首先啟動帶一個參數(shù)的python命令,參數(shù)為python腳本的絕對路徑。可以放在java工程的resources目錄下。需要注意的是:redirectErrorStream(true),為了使得當執(zhí)行腳本出現(xiàn)錯誤時,錯誤輸出流被合并至標準輸出流。這樣設置可以從Process對象的getInputStream()方法中讀取錯誤信息。如果沒有該設置,則需要分別用兩個方法獲取流:getInputStream() 和 getErrorStream() 。processBuilder.start()獲取Process對象,然后讀取輸出流并驗證結果。
使用Java腳本引擎
java6首次引入JSR-223規(guī)范,定義一組提供基本腳本功能的腳本API。這些API提供了執(zhí)行腳本和在Java和腳本語言之間共享值的機制。該規(guī)范主要目的是為了統(tǒng)一Java與不同實現(xiàn)JVM的動態(tài)腳本語言的交互,Jython是在jvm上運行python的java實現(xiàn)。假設我們在CLASSPATH上有Jython,框架自動發(fā)現(xiàn)我們有可能使用該腳本引擎,并允許我們直接請求Python腳本引擎。因為Maven有Jython,我們可以在maven中引用,當然也下載直接安裝:
<dependency>
<groupId>org.python</groupId>
<artifactId>jython</artifactId>
<version>2.7.2</version>
</dependency>
可以通過下面代碼列出所有支持的腳本引擎:
public static void listEngines() {
ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> engines = manager.getEngineFactories();
for (ScriptEngineFactory engine : engines) {
LOGGER.info("Engine name: {}", engine.getEngineName());
LOGGER.info("Version: {}", engine.getEngineVersion());
LOGGER.info("Language: {}", engine.getLanguageName());
LOGGER.info("Short Names:");
for (String names : engine.getNames()) {
LOGGER.info(names);
}
}
}
如果Jython在環(huán)境中可用,應該看到相應的輸出:
... Engine name: jython Version: 2.7.2 Language: python Short Names: python jython
現(xiàn)在使用Jython調(diào)用hello.py腳本:
@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 Python!", writer.toString().trim());
}
使用該API比上面的示例更簡潔。首先設置ScriptContext包含StringWriter,用于保存執(zhí)行腳本的輸出。然后提供簡稱讓ScriptEngineManager 查找腳本引擎,可以使用python或jython。最后驗證輸出是否與期望一致。
其實也可以使用PythonInterpretor 類直接調(diào)用嵌入的python代碼:
@Test
public void givenPythonInterpreter_whenPrintExecuted_thenOutputDisplayed() {
try (PythonInterpreter pyInterp = new PythonInterpreter()) {
StringWriter output = new StringWriter();
pyInterp.setOut(output);
pyInterp.exec("print('Hello Python!')");
assertEquals("Should contain script output: ", "Hello Python!", output.toString().trim());
}
}
使用PythonInterpreter類,可以通過exec方法直接執(zhí)行python代碼。和前面示例一樣通過StringWriter 捕獲執(zhí)行輸出。下面再看一個示例:
@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());
}
}
上面示例可以使用get方法訪問變量值。下面示例看如何捕獲錯誤:
try (PythonInterpreter pyInterp = new PythonInterpreter()) {
pyInterp.exec("import syds");
}
運行上面代碼會拋出PyException 異常,與在本地執(zhí)行Python腳本輸出錯誤一樣。
下面有幾點注意事項:
- PythonIntepreter 實現(xiàn)了AutoCloseable,最好與 try-with-resources 一起使用。
- PythonIntepreter類名不是表示Python代碼的解析器,Python程序在Jython是運行在jvm中,執(zhí)行前需要編譯為java字節(jié)碼。
- 盡管Jython是Java的Python實現(xiàn),但它可能不包含與本機Python相同的所有子包。
下面示例展示如何把java變量賦給Python變量:
import org.python.util.PythonInterpreter;
import org.python.core.*;
class test3{
public static void main(String a[]){
int number1 = 10;
int number2 = 32;
try (PythonInterpreter pyInterp = new PythonInterpreter()) {
python.set("number1", new PyInteger(number1));
python.set("number2", new PyInteger(number2));
python.exec("number3 = number1+number2");
PyObject number3 = python.get("number3");
System.out.println("val : "+number3.toString());
}
}
}
總結
本文介紹了如何從Java調(diào)用Python腳本,使用jython腳本引擎比ProcessBuilder類更簡單。另外Python可以便捷搭建http應用,Java也可以通過HTTP協(xié)議直接調(diào)用HTTP服務實現(xiàn)交互。
到此這篇關于Java調(diào)用Python代碼的幾種方法小結的文章就介紹到這了,更多相關Java調(diào)用Python代碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring?Boot?Reactor?整合?Resilience4j詳析
這篇文章主要介紹了Spring?Boot?Reactor整合Resilience4j詳析,文章通過引入pom包展開詳細介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-09-09
Android/Java中創(chuàng)建類實例的各種模式實例代碼
這篇文章主要介紹了Android/Java中創(chuàng)建類實例各種模式的相關資料,包括New關鍵字、靜態(tài)工廠方法、建造者模式、單例模式、依賴注入、抽象工廠模式、原型模式和Reflection,每個模式有其特點和適用場景,需要的朋友可以參考下2025-09-09
如何使用nexus在局域網(wǎng)內(nèi)搭建maven私服及idea的使用
這篇文章主要介紹了如何使用nexus在局域網(wǎng)內(nèi)搭建maven私服及idea的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-11-11
springboot實現(xiàn)多實例crontab搶占定時任務(實例代碼)
這篇文章主要介紹了springboot實現(xiàn)多實例crontab搶占定時任務,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2020-01-01
JavaGUI界面實現(xiàn)頁面跳轉(zhuǎn)方法
這篇文章主要給大家介紹了關于JavaGUI界面實現(xiàn)頁面跳轉(zhuǎn)的相關資料, GUI是指圖形用戶界面,指采用圖形方式顯示的計算機操作用戶界面,需要的朋友可以參考下2023-07-07

