如何从 Java 调用 Python-3种方法[spring boot等]
Posted 红桃xin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何从 Java 调用 Python-3种方法[spring boot等]相关的知识,希望对你有一定的参考价值。
如何从 Java 调用 Python-3种方法
1. 概述
Python是一种越来越流行的编程语言,特别是在科学界,因为它具有丰富多样的数值和统计包。因此,能够从我们的 Java 应用程序调用 Python 代码的要求并不少见。
在本教程中,我们将了解一些从 Java 调用 Python 代码的最常见方法。
2. 一个简单的 Python 脚本
在本教程中,我们将使用一个非常简单的 Python 脚本,我们将在名为hello.py的专用文件中定义该脚本:
print("Hello Baeldung Readers!!")
假设我们有一个有效的 Python 安装,当我们运行我们的脚本时,我们应该看到打印的消息:
$ python hello.py
Hello Baeldung Readers!!
3. 核心Java
在本节中,我们将看一下可用于使用核心 Java 调用 Python 脚本的两个不同选项。
3.1. 使用流程构建器
让我们首先看看我们如何使用ProcessBuilder API创建一个本地操作系统进程来启动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<String> 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)的调用也很重要 。如果出现任何错误,错误输出将与标准输出合并。
这很有用,因为这意味着当我们在Process对象上调用getInputStream()方法时,我们可以从相应的输出中读取任何错误消息。如果我们不将此属性设置为true,那么我们将需要使用getInputStream()和getErrorStream()方法从两个单独的流中读取输出。
现在,我们使用start() 方法启动进程以获取 Process对象。然后我们读取流程输出并验证内容是否符合我们的预期。
如前所述,我们假设python 命令可通过 PATH变量使用。
3.2. 使用 JSR-223 脚本引擎
JSR-223首次在 Java 6 中引入,定义了一组提供基本脚本功能的脚本 API。这些方法提供了执行脚本和在 Java 和脚本语言之间共享值的机制。该标准的主要目标是尝试为与 Java 不同脚本语言的互操作带来一定的一致性。
我们可以将可插拔脚本引擎架构用于任何具有JVM实现的动态语言,当然。Jython是在 JVM 上运行的 Python 的 Java 平台实现。
假设我们在CLASSPATH上有 Jython ,框架应该会自动发现我们有可能使用这个脚本引擎,并使我们能够直接请求 Python 脚本引擎。
由于 Jython 可从Maven Central 获得,我们可以将它包含在我们的 pom.xml 中:
<dependency>
<groupId>org.python</groupId>
<artifactId>jython</artifactId>
<version>2.7.2</version>
</dependency>
同样,也可以直接下载安装。
让我们列出所有可用的脚本引擎:
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 非常简单。首先,我们首先设置一个包含StringWriter的ScriptContext。这将用于存储我们要调用的脚本的输出。
然后,我们使用ScriptEngineManager类的getEngineByName方法为给定的短名称查找和创建ScriptEngine。在我们的例子中,我们可以传递python或jython,这是与这个引擎关联的两个短名称。
和以前一样,最后一步是从我们的脚本中获取输出并检查它是否符合我们的预期。
4. Jython
继续使用 Jython,我们还可以将 Python 代码直接嵌入到我们的 Java 代码中。我们可以使用PythonInterpretor类来做到这一点:
@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类允许我们通过exec方法执行一串 Python 源代码。和之前一样,我们使用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”);
}
当我们运行这段代码时,会抛出一个PyException异常,我们会看到与使用原生 Python 一样的错误:
Traceback (most recent call last):
File “”, line 1, in
ImportError: No module named syds
我们应该注意以下几点:
由于PythonIntepreter实现了AutoCloseable,因此在使用此类时最好使用try-with-resources
该PythonInterpreter类名称并不意味着我们的Python代码解释。Jython 中的 Python 程序由 JVM 运行,因此在执行前编译为 Java 字节码
尽管 Jython 是 Java 的 Python 实现,但它可能不包含与原生 Python 相同的所有子包
5. Apache Commons Exec
我们可以考虑使用的另一个第三方库是Apache Common Exec,它试图克服Java Process API 的一些缺点。
该公地EXEC神器可从Maven的中央:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
现在让我们来看看如何使用这个库:
@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 的例子并没有太大的不同。我们为给定的命令创建一个CommandLine对象。接下来,我们设置了一个流处理程序,用于在执行我们的命令之前捕获我们进程的输出。
总而言之,该库背后的主要理念是提供一个流程执行包,旨在通过一致的 API 支持各种操作系统。
6. 利用 HTTP 实现互操作性
让我们退后一步,与其尝试直接调用 Python,不如考虑使用一个完善的协议(如 HTTP)作为两种不同语言之间的抽象层。
实际上,Python 附带了一个简单的内置 HTTP 服务器,我们可以使用它来通过 HTTP 共享内容或文件:
python -m http.server 9000
如果我们现在转到http://localhost:9000,我们将看到为我们启动上一个命令的目录列出的内容。
我们可以考虑使用其他一些流行的框架来创建更强大的基于 Python 的 Web 服务或应用程序,例如Flask和Django。
一旦我们有了可以访问的端点,我们就可以使用多个Java HTTP库中的任何一个来调用我们的 Python Web 服务/应用程序实现。
7. 结论
在本教程中,我们了解了一些最流行的从 Java 调用 Python 代码的技术。
与往常一样,本文的完整源代码可 在 GitHub 上找到。
翻译引用自
以上是关于如何从 Java 调用 Python-3种方法[spring boot等]的主要内容,如果未能解决你的问题,请参考以下文章