运行 Python 脚本到 Java 只导入一次

Run Python script through Java with importing just once

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


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

import sys
import special-library

def fun(x):
  do something

fun(sys.argv[1])

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

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

第一次尝试实施

我试过写一个 'pipe' Java Class 来执行一次 python 脚本。现在脚本从 stdin 连续读取,当它获得输入 argument 时,它会进行计算并 returns 结果:

import sys
import special-library

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

for arg in sys.stdin:
  fun(arg)

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

JavaClass如下:

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;
    }
}

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

new MPBNPythonScriptExecuter();

以后当我想返回一些结果时,我使用:

String arg = "something"
Arraylist<String> res = PythonScriptExecuter.getResults(arg);

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

while (!(line = pythonToJavaReader.readLine()).equals("END"))

知道这里出了什么问题吗?

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

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

Python 脚本

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

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

  2. 你调用你的函数

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

Java

  1. 写一个发送参数到python

  2. 的方法
  3. 将函数的参数写入管道

  4. 从管道中读取答案

大功告成。

这是一些片段

这里是你如何创建你的管子

        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()));
}

在您的 Python 脚本中尝试以下操作:

import sys

def fun(x):
  print(x)
  print('END')
  sys.stdout.flush()

while 1:
    try:
        line = sys.stdin.readline()
    except KeyboardInterrupt:
        break

    if not line:
        break

    fun(line)

您的 Java 代码应该或多或少是正确的。