Запустить Python скрипт через Java с импортом всего один раз - PullRequest
3 голосов
/ 07 апреля 2020

Обновление за апрель 2020 : я принял ответ ниже, потому что он предлагает очень хорошее и простое решение моей проблемы, но я так и не получил свой код на работу! Если кто-то уже создал что-то подобное, пожалуйста, свяжитесь со мной!


У меня есть простой скрипт my-script.py, который очень быстро выполняет некоторые вычисления, используя указанную библиотеку c, написанную на 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)? Кто-нибудь делал что-то подобное или есть какое-то другое решение?

Первая попытка реализации

Я пытался написать класс 'pipe' 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"))

Есть идеи, что здесь не так?

Ответы [ 2 ]

6 голосов
/ 11 апреля 2020

Вы можете связываться между java и Python с помощью каналов.

Вы запускаете Python, как и сейчас (без аргументов командной строки)

Python script

вы пишете бесконечное l oop в python, которое будет

чтение данных со стандартного входа (это будет ваш аргумент для функции)

Вы вызываете свою функцию

вы пишете ответ на стандартный вывод

Java

  1. Пишите способ отправки аргументов на python

  2. напишите в трубу аргумент вашей функции

  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()));
}
0 голосов
/ 18 апреля 2020

Попробуйте следующее в вашем 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 должен быть более или менее правильным.

...