首頁 > Java > java教程 > 主體

Java中如何呼叫Python

WBOY
發布: 2023-05-17 12:08:12
轉載
2286 人瀏覽過

    Python語言有豐富的系統管理、資料處理、統計類別軟體包,因此從java應用程式呼叫Python程式碼的需求很常見、實用。 DataX 是阿里開源的異質資料來源離線同步工具,致力於實現包含關聯式資料庫(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各種異質資料來源之間穩定且有效率的數據同步功能。 Datax也是透過Java呼叫Python腳本。

    Java core

    Java提供了有兩種方法,分別為ProcessBuilder API和 JSR-223 Scripting Engine。

    使用ProcessBuilder

    透過ProcessBuilder建立本機作業系統進程啟動python並執行Python腳本, hello.py腳本簡單輸出「Hello Python!」。需要開發環境已經安裝了python,並設定了環境變數。

    @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();
    }
    登入後複製

    要重寫這句話可這樣說: 使用帶有一個參數的Python指令來啟動,該參數是Python腳本的完整路徑。可以放在java工程的resources目錄下。要注意的是:redirectErrorStream(true),為了使得當執行腳本出現錯誤時,錯誤輸出流會合併到標準輸出流。可以透過呼叫Process物件的getInputStream()方法來讀取錯誤訊息。如果沒有該設置,則需要分別用兩個方法取得流:getInputStream() 和 getErrorStream() 。從ProcessBuilder中取得Process物件後,透過讀取輸出流來驗證結果。

    使用Java腳本引擎

    JSR-223規格是Java 6首次引入的,該規格定義了一組腳本API,可以提供基本腳本功能。這些API提供了在Java和腳本語言之間共享值及執行腳本的機制。這個規格主要目的是為了統一Java與不同實作JVM的動態腳本語言的交互,Jython是在jvm上執行python的java實作。假設我們在CLASSPATH上有Jython,框架自動發現我們有可能使用該腳本引擎,並允許我們直接請求Python腳本引擎。在Maven中,我們可以引用Jython,也可以直接下載安裝它

    <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在環境中可用,應該要看到對應的輸出:

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

    現在使用Jython呼叫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包含其中,以便儲存腳本執行的輸出。然後提供簡稱讓ScriptEngineManager 尋找腳本引擎,可以使用python或jython。最後驗證輸出是否與期望一致。

    其實也可以使用PythonInterpretor 類別直接呼叫嵌入的python程式碼:

    @Test
    public void givenPythonInterpreter_whenPrintExecuted_thenOutputDisplayed() {
        try (PythonInterpreter pyInterp = new PythonInterpreter()) {
            StringWriter output = new StringWriter();
            pyInterp.setOut(output);
            pyInterp.exec("print(&#39;Hello Python!&#39;)");
            assertEquals("Should contain script output: ", "Hello Python!", output.toString().trim());
        }
    }
    登入後複製

    PythonInterpreter類別提供的exec方法可直接執行Python程式碼。和前面範例一樣透過StringWriter 捕獲執行輸出。下面再看一個範例:

    @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 異常,與在本機執行Python腳本輸出錯誤一樣。

    下面有幾點注意事項:

    • PythonIntepreter 實作了AutoCloseable,最好是與 try-with-resources 一起使用。

    • PythonIntepreter類別名稱不是表示Python程式碼的解析器,Python程式在Jython運行在jvm中,執行前需要編譯為java位元組碼。

    • 儘管Jython是Java的Python實現,但它可能不包含與本機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中如何呼叫Python的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    相關標籤:
    來源:yisu.com
    本網站聲明
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
    熱門教學
    更多>
    最新下載
    更多>
    網站特效
    網站源碼
    網站素材
    前端模板