Рубрики
Без рубрики

Как вызвать Python Из Java

Изучите наиболее распространенные способы вызова кода Python из Java.

Автор оригинала: Jonathan Cook.

1. Обзор

Python становится все более популярным языком программирования, особенно в научном сообществе из-за его богатого разнообразия числовых и статистических пакетов. Поэтому нет ничего необычного в том, чтобы иметь возможность вызывать код Python из наших Java-приложений.

В этом уроке мы рассмотрим некоторые из наиболее распространенных способов вызова кода Python из Java.

2. Простой Скрипт На Python

В этом уроке мы будем использовать очень простой скрипт на Python, который мы определим в специальном файле с именем hello.py :

print("Hello Baeldung Readers!!")

Предполагая, что у нас есть работающая установка Python, при запуске нашего скрипта мы должны увидеть напечатанное сообщение:

$ python hello.py 
Hello Baeldung Readers!!

3. Ядро Java

В этом разделе мы рассмотрим два различных варианта, которые мы можем использовать для вызова нашего скрипта Python с использованием ядра Java.

3.1. Использование ProcessBuilder

Давайте сначала рассмотрим, как мы можем использовать API ProcessBuilder для создания собственного процесса операционной системы для запуска python и выполнения нашего простого сценария:

@Test
public void givenPythonScript_whenPythonProcessInvoked_thenSuccess() throws Exception {
    ProcessBuilder processBuilder = new ProcessBuilder("python", resolvePythonScriptPath("hello.py"));
    processBuilder.redirectErrorStream(true);

    Process process = processBuilder.start();
    List results = readProcessOutput(process.getInputStream());

    assertThat("Results should not be empty", results, is(not(empty())));
    assertThat("Results should contain output of script: ", results, hasItem(
      containsString("Hello Baeldung Readers!!")));

    int exitCode = process.waitFor();
    assertEquals("No errors should be detected", 0, exitCode);
}

В этом первом примере мы запускаем команду python с одним аргументом, который является абсолютным путем к вашему hello.py сценарий. Мы можем найти его в нашей папке test/resources .

Подводя итог, мы создаем наш объект ProcessBuilder , передавая значения команды и аргумента конструктору. Также важно упомянуть вызов redirectErrorStream(true). В случае каких-либо ошибок вывод ошибок будет объединен со стандартным выводом.

Это полезно, поскольку означает, что мы можем считывать любые сообщения об ошибках из соответствующего вывода при вызове метода getInputStream() для объекта Process . Если мы не установим этому свойству значение true , то нам нужно будет считывать выходные данные из двух отдельных потоков, используя методы getInputStream() и getErrorStream () .

Теперь мы запускаем процесс с помощью метода start () , чтобы получить объект Process . Затем мы читаем выходные данные процесса и проверяем, что содержимое соответствует нашим ожиданиям.

Как уже упоминалось ранее, мы сделали предположение, что питон команда доступна через ПУТЬ переменная.

3.2. Работа Со Скриптовым Движком JSR-223

JSR-223 , который был впервые представлен в Java 6, определяет набор API-интерфейсов сценариев, которые обеспечивают базовую функциональность сценариев. Эти методы предоставляют механизмы для выполнения сценариев и обмена значениями между Java и языком сценариев. Основная цель этого стандарта состояла в том, чтобы попытаться привнести некоторое единообразие во взаимодействие с различными языками сценариев из Java.

Мы можем использовать подключаемую архитектуру скриптового движка для любого динамического языка при условии, конечно, что он имеет реализацию JVM . Jython – это реализация платформы Java Python, которая работает на JVM.

Предполагая , что у нас есть Jython в CLASSPATH , фреймворк должен автоматически обнаружить, что у нас есть возможность использовать этот механизм сценариев, и позволить нам напрямую запросить механизм сценариев Python.

Поскольку Jython доступен из Maven Central , мы можем просто включить его в ваш pom.xml :


    org.python
    jython
    2.7.2

Аналогично, он также может быть загружен и установлен напрямую.

Давайте перечислим все скриптовые движки, которые у нас есть в наличии:

ScriptEngineManagerUtils.listEngines();

Если у нас есть возможность использовать Jython, мы должны увидеть соответствующий скриптовый движок:

...
Engine name: jython
Version: 2.7.2
Language: python
Short Names:
python
jython

Теперь, когда мы знаем, что можем использовать скриптовый движок Jython, давайте продолжим и посмотрим, как вызвать hello.py скрипт:

@Test
public void givenPythonScriptEngineIsAvailable_whenScriptInvoked_thenOutputDisplayed() throws Exception {
    StringWriter writer = new StringWriter();
    ScriptContext context = new SimpleScriptContext();
    context.setWriter(writer);

    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("python");
    engine.eval(new FileReader(resolvePythonScriptPath("hello.py")), context);
    assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", writer.toString().trim());
}

Как мы видим, работать с этим API довольно просто. Во-первых, мы начнем с настройки ScriptContext , который содержит StringWriter . Это будет использоваться для хранения выходных данных скрипта, который мы хотим вызвать.

Затем мы используем метод getEngineByName класса ScriptEngineManager для поиска и создания скриптового движка для заданного короткого имени . В нашем случае мы можем передать python или jython , которые являются двумя короткими именами, связанными с этим движком.

Как и прежде, последний шаг-получить выходные данные из нашего скрипта и проверить, соответствует ли он тому, что мы ожидали.

4. Джайтон

Продолжая работу с Jython, у нас также есть возможность встраивать код Python непосредственно в наш Java-код. Мы можем сделать это с помощью класса Pythoninterpreter :

@Test
public void givenPythonInterpreter_whenPrintExecuted_thenOutputDisplayed() {
    try (PythonInterpreter pyInterp = new PythonInterpreter()) {
        StringWriter output = new StringWriter();
        pyInterp.setOut(output);

        pyInterp.exec("print('Hello Baeldung Readers!!')");
        assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", output.toString()
          .trim());
    }
}

Использование класса PythonInterpreter позволяет нам выполнить строку исходного кода Python с помощью метода exec /. Как и прежде, мы используем StringWriter для захвата выходных данных этого выполнения.

Теперь давайте рассмотрим пример, где мы складываем два числа вместе:

@Test
public void givenPythonInterpreter_whenNumbersAdded_thenOutputDisplayed() {
    try (PythonInterpreter pyInterp = new PythonInterpreter()) {
        pyInterp.exec("x = 10+10");
        PyObject x = pyInterp.get("x");
        assertEquals("x: ", 20, x.asInt());
    }
}

В этом примере мы видим, как мы можем использовать метод get для доступа к значению переменной.

В нашем последнем примере Jython мы увидим, что происходит, когда возникает ошибка:

try (PythonInterpreter pyInterp = new PythonInterpreter()) {
    pyInterp.exec("import syds");
}

Когда мы запускаем этот код, возникает Исключение , и мы увидим ту же ошибку, как если бы мы работали с собственным Python:

Traceback (most recent call last):
  File "", line 1, in 
ImportError: No module named syds

Несколько моментов, которые мы должны отметить:

  • Поскольку Интерпретатор Python реализует Автоклавируемый , рекомендуется использовать try-with-resources при работе с этим классом
  • Имя класса PythonInterpreter не означает, что наш код Python интерпретируется. Программы Python в Python запускаются JVM и поэтому компилируются в байт-код Java перед выполнением
  • Хотя Jython является реализацией Python для Java, он может содержать не все те же подпакеты, что и родной Python

5. Apache Commons Exec

Еще одной сторонней библиотекой, которую мы могли бы рассмотреть, является Apache Common Exec , которая пытается преодолеть некоторые недостатки API процесса Java|/.

Артефакт commons-exec доступен в Maven Central :


    org.apache.commons
    commons-exec
    1.3

Теперь давайте посмотрим, как мы можем использовать эту библиотеку:

@Test
public void givenPythonScript_whenPythonProcessExecuted_thenSuccess() 
  throws ExecuteException, IOException {
    String line = "python " + resolvePythonScriptPath("hello.py");
    CommandLine cmdLine = CommandLine.parse(line);
        
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
        
    DefaultExecutor executor = new DefaultExecutor();
    executor.setStreamHandler(streamHandler);

    int exitCode = executor.execute(cmdLine);
    assertEquals("No errors should be detected", 0, exitCode);
    assertEquals("Should contain script output: ", "Hello Baeldung Readers!!", outputStream.toString()
      .trim());
}

Этот пример не слишком отличается от нашего первого примера с использованием ProcessBuilder . Мы создаем объект командной строки для данной команды. Затем мы настроим обработчик потока, который будет использоваться для захвата выходных данных нашего процесса перед выполнением вашей команды.

Подводя итог, можно сказать, что основная философия этой библиотеки заключается в том, чтобы предложить пакет выполнения процессов, направленный на поддержку широкого спектра операционных систем с помощью согласованного API.

6. Использование HTTP для взаимодействия

Давайте сделаем шаг назад на мгновение и вместо того, чтобы пытаться напрямую вызывать Python, рассмотрим использование хорошо зарекомендовавшего себя протокола, такого как HTTP, в качестве уровня абстракции между двумя разными языками.

На самом деле Python поставляется с простым встроенным HTTP-сервером, который мы можем использовать для обмена контентом или файлами по HTTP :

python -m http.server 9000

Если мы сейчас перейдем к http://localhost:9000 , мы увидим содержимое каталога, в котором мы запустили предыдущую команду.

Некоторые другие популярные фреймворки, которые мы могли бы использовать для создания более надежных веб-сервисов или приложений на основе Python, Колба и Джанго .

Как только у нас есть конечная точка, к которой мы можем получить доступ, мы можем использовать любую из нескольких библиотек Java HTTP для вызова нашей реализации веб-службы/приложения Python.

7. Заключение

В этом уроке мы узнали о некоторых из самых популярных технологий для вызова кода Python из Java.

Как всегда, полный исходный код статьи доступен на GitHub .