【问题标题】:Run Python script through Java with importing just once通过 Java 运行 Python 脚本,只需导入一次
【发布时间】:2020-07-19 02:14:05
【问题描述】:

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


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

import sys
import special-library

def fun(x):
  do something

fun(sys.argv[1])

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

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

首次尝试实施

我尝试编写一个“管道”Java 类,它会执行一次 python 脚本。现在脚本从标准输入连续读取,当它得到输入argument时,它会进行计算并返回结果:

import sys
import special-library

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

for arg in sys.stdin:
  fun(arg)

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

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

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

new MPBNPythonScriptExecuter();

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

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

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

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

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

【问题讨论】:

  • 你确认你的python脚本没有错误吗?也需要用类似这样的东西来捕捉错误 while ((ligne = error.readLine()) != null) { System.out.println("err: "+ ligne); }
  • 不,它更像是它没有以某种方式正确读取(可能在正确的时间)......当我杀死 java 程序时,有时我可以看到 while 循环内的输出(我打印line) - 但只有到那时 - 就像它被卡住了一样......

标签: java python import jython


【解决方案1】:

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

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

Python 脚本

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

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

  2. 你调用你的函数

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

Java

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

  2. 将函数的 arg 写入管道

  3. 从管道中读取答案

你已经完成了。

这里有一些sn-p

这里是你如何创建你的管

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

【讨论】:

  • 听起来很有希望!我将在此基础上制定解决方案,并会回复您!
  • 你也可以避免 .exec() 它只会执行一次,就像 import 一样
  • 这种情况下脚本的这个功能不能并行请求吧?例如。两个进程同时调用函数(同一个进程p),由于脚本写入stdout,你不知道哪个结果属于哪个。也许 Java 中的 synchronized 在这种情况下会有所帮助,会检查。
  • 嗨帕斯卡!我遇到了一个问题,并用我目前的方法编辑了这个问题。可以的话可以查一下吗?
  • 如果您需要并行调用,那么最好采用客户端/服务器方法,但我认为这对于您的需求来说会非常复杂。带有 IO 管道的解决方案是顺序的,您需要使其安全并将同步添加到您的 getResults 方法
【解决方案2】:

在您的 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 代码应该或多或少是正确的。

【讨论】:

  • 感谢您的努力!问题仍然存在,我认为主要与 Java 部分有关。
猜你喜欢
  • 2015-10-06
  • 1970-01-01
  • 2011-02-26
  • 2013-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-16
  • 1970-01-01
相关资源
最近更新 更多