只需导入一次即可通过 Java 运行 Python 脚本

Joh*_*ohn 3 python java import jython

2020 年 4 月更新:我接受了下面的答案,因为它为我的问题提出了一个非常好的和简单的解决方案,但我的代码从未起作用!如果有人已经建立了类似的东西,请联系我!


我有一个简单的脚本my-script.py,它使用编写的特定库非常快速地进行一些计算Python

import sys
import special-library

def fun(x):
  do something

fun(sys.argv[1])
Run Code Online (Sandbox Code Playgroud)

在我的Java代码中,我在代码的不同部分多次使用/调用此脚本,使用该ProcessBuilder方法,这意味着我只需运行命令python my-script.py argument。这意味着每次调用这个脚本时,我都必须执行import其中最耗时的命令(计算部分实际上更快)。

有没有办法让我只调用一次导入?我看了一点Jython- 是否可以编写一个类或一些可以启动的东西并进行一次导入,然后每次我想做计算部分(fun)时调用它的函数?有没有人做过类似的事情或对此有任何其他解决方案?

首次尝试实施

我试图编写一个“管道”Java 类,它将执行一次 python 脚本。现在脚本从 stdin 连续读取,当它获得 input 时argument,它会进行计算并返回结果:

import sys
import special-library

def fun(x):
  do something
  print('END')

for arg in sys.stdin:
  fun(arg)
Run Code Online (Sandbox Code Playgroud)

当我从命令行测试它并为其提供输入参数时,这当然有效。

Java类如下:

package my.package;
import java.io.*;

public class PythonScriptExecuter {

    private static BufferedReader pythonToJavaReader;
    private static PrintWriter javaToPythonWriter;

    public PythonScriptExecuter() throws Exception {
        String MPBNScriptFile = "fullPathToScriptFile";
        ProcessBuilder pb = new ProcessBuilder("python", MPBNScriptFile);
        pb.redirectErrorStream(true);

        // Start script
        Process p = pb.start();

        pythonToJavaReader = getOutputReader(p); // from python to java
        javaToPythonWriter = getInputWriter(p); // from java to python
    }

     // Python => Java
    private static BufferedReader getOutputReader(Process p) {
        return new BufferedReader(new InputStreamReader(p.getInputStream()));
    }

    // Java => Python
    private static PrintWriter getInputWriter(Process p) {
        return new PrintWriter(new OutputStreamWriter(p.getOutputStream()));
    }

    public static Arraylist<String> getResults(String argument) throws IOException {
        // send argument to python script
        javaToPythonWriter.println(argument);
        //javaToPythonWriter.flush();

        // get results back one by one
        ArrayList<String> results = new ArrayList<>();

        int count = 0;
        String line;
        while (!(line = pythonToJavaReader.readLine()).equals("END")) {
            // process `line` string 
            results.add(line);
        }

        return results;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,我所做的是在我代码的初始化部分的某个地方,我有这个简单的行来启动这个过程:

new MPBNPythonScriptExecuter();
Run Code Online (Sandbox Code Playgroud)

后来当我想得到一些结果时,我使用:

String arg = "something"
Arraylist<String> res = PythonScriptExecuter.getResults(arg);
Run Code Online (Sandbox Code Playgroud)

整个事情都挂在从脚本读取输出的行上:

while (!(line = pythonToJavaReader.readLine()).equals("END"))
Run Code Online (Sandbox Code Playgroud)

任何想法这里出了什么问题?

Pas*_*res 6

您可以使用管道在 java 和 Python 之间进行通信。

你像现在一样运行你的 Python(没有命令行参数)

Python脚本

你在 python 中编写了一个无限循环,它将

  1. 从标准输入读取数据(它将是您的函数参数)

  2. 你调用你的函数

  3. 你把答案写到标准输出

爪哇

  1. 写一个向python发送args的方法

  2. 将函数的 arg 写入管道

  3. 从管道中读取答案

你完成了。

这是一些片段

在这里你如何创建你的管

        Process p = Runtime.getRuntime().exec(commande);
        BufferedReader output = getOutput(p); //from python to java
        BufferedReader error = getError(p); //from python to java
        PrintWriter input  = getInput(p); //from java to python

private static BufferedReader getOutput(Process p) {
    return new BufferedReader(new InputStreamReader(p.getInputStream()));
}
private static BufferedReader getError(Process p) {
    return new BufferedReader(new InputStreamReader(p.getErrorStream()));
}    
private static PrintWriter getInput(Process p){
    return new PrintWriter (new OutputStreamWriter(p.getOutputStream()));
}
Run Code Online (Sandbox Code Playgroud)