简体   繁体   English

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

[英]Run Python script through Java with importing just once

Update April 2020 : I have accepted the answer below because it proposes a very good and simple solution to my problem but I never got my code to work! 2020 年 4 月更新:我接受了下面的答案,因为它为我的问题提出了一个非常好的和简单的解决方案,但我的代码从来没有工作过! If anyone has already built something similar please contact me!如果有人已经建立了类似的东西,请联系我!


I have a simple script my-script.py that does some calculations very fast using a specific library written in Python :我有一个简单的脚本my-script.py使用Python编写的特定库非常快速地进行一些计算:

import sys
import special-library

def fun(x):
  do something

fun(sys.argv[1])

In my Java code, I use/call this script a lot in different part of the code, using the ProcessBuilder approach, which means that I simply run the command python my-script.py argument .在我的Java代码中,我在代码的不同部分使用/调用此脚本很多,使用ProcessBuilder方法,这意味着我只需运行命令python my-script.py argument This means that every time I call this script, I have to execute the import commands which is the most time-consuming thing in this (the calculation part is actually faster).这意味着每次我调用这个脚本时,我都必须执行import命令,这是其中最耗时的事情(计算部分实际上更快)。

Is there a solution so that I call the import just once ?有没有办法让我只调用一次导入 I looked a little bit about Jython - would be possible to write a class or something that would be initiated and do the import once and then calling a function of it every time I want to do the calculations part ( fun )?我看了一点关于Jython的信息 - 是否可以编写 class 或一些可以启动并执行一次导入然后每次我想做计算部分时调用它的 function 的东西( fun )? Has anyone done something similar or have any other solution to this?有没有人做过类似的事情或有任何其他解决方案?

First Attempt at Implementation首次尝试实施

I've tried to write a 'pipe' Java Class that will execute the python script once.我试图编写一个“管道” Java Class 将执行 python 脚本一次。 Now the script reads continuously from the stdin and when it gets an input argument , it does the calculations and returns the result:现在脚本从标准输入连续读取,当它获得输入argument时,它会进行计算并返回结果:

import sys
import special-library

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

for arg in sys.stdin:
  fun(arg)

And this works of course when I test it from the command line and supplying it with input arguments.当我从命令行对其进行测试并为其提供输入 arguments 时,这当然有效。

The Java Class is as follows: Java Class如下:

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

So, what I do is that somewhere in the initialization part of my code I have this simple line to start the process:所以,我所做的是在我的代码的初始化部分的某个地方,我有这个简单的行来启动这个过程:

new MPBNPythonScriptExecuter();

and later when I want to get some results back, I use:后来当我想得到一些结果时,我使用:

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

and the whole thing hangs on the reading-the-output-from-the-script line:整个事情都挂在从脚本读取输出的行上:

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

Any ideas what is going wrong here?任何想法这里出了什么问题?

You could communicate between java and Python with pipes.您可以使用管道在 java 和 Python 之间进行通信。

You run your Python as you do now (without command line args)您像现在一样运行 Python(没有命令行参数)

Python script Python 脚本

you write an infinite loop in python that will您在 python 中编写了一个无限循环,它将

  1. read data from the standard input (it will be your arg for the function)从标准输入读取数据(这将是函数的参数)

  2. You call your function你打电话给你的 function

  3. you write the answer to the standard output你写下标准 output 的答案

Java Java

  1. Write a method for sending args to python编写发送args到python的方法

  2. write to the pipe the arg of your function将 function 的 arg 写入 pipe

  3. read the answer from the pipe从 pipe 中阅读答案

You're done.你完成了。

Here is some snippet这是一些片段

Here how you create your tube在这里你如何创建你的管

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

Try the following in your Python script:在您的 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)

Your Java code should be more or less correct.您的 Java 代码应该或多或少是正确的。

I did a similar project.我做了一个类似的项目。 The options we had was Jython, Jep, graalVM and socket technology.我们的选择是 Jython、Jep、graalVM 和套接字技术。 Indeed, I have never tested Jython because I need to call numpy which is not possible.事实上,我从未测试过 Jython,因为我需要调用 numpy,这是不可能的。 Jep looks promising but you have to build it for specific version, and I could not make it work. Jep 看起来很有希望,但你必须为特定版本构建它,我无法让它工作。 GraalVM is very nice, however It did not exist at that time and (it is not available for windows, but don't worry you can make the binary) and it is still experimental feature. GraalVM 非常好,但是它当时并不存在并且(它不适用于 windows,但不用担心你可以制作二进制文件)并且它仍然是实验性功能。 So the only option was sockets technology.所以唯一的选择是 sockets 技术。 you can easily make a socket in java, and likewise in python.您可以轻松地在 java 中制作插座,同样在 python 中制作插座。 Then write a set of function to convert string to primitive arrays and wise versa.然后编写一组 function 将字符串转换为原始 arrays ,反之亦然。 The drawback is needing compilation against your machines python and the time for communications.缺点是需要针对您的机器 python 和通信时间进行编译。 I put the project on git, I hope you will find it useful.我把项目放在git上,希望对你有用。
the Project's repo: https://github.com/amirshamaei/HLSVDPro4J项目的回购: https://github.com/amirshamaei/HLSVDPro4J

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM