Java执行Python代码的两种方法(Jython与ProcessBuilder)

Java执行python代码有很多种方法,这里介绍两种,Jython和ProcessBuilder,并简要介绍下它们的优缺点。


使用Jython执行python代码

Jython说明

Jython官网对于Jython的介绍是这样的:

The Jython project provides implementations of Python in Java, providing to Python the benefits of running on the JVM and access to classes written in Java. The current release (a Jython 2.7.x) only supports Python 2 (sorry). There is work towards a Python 3 in the project’s GitHub repository.

可以看到,目前Jython最新版本也仅支持Python 2.x,不支持Python 3及更高版本,所以如果要执行Python 3的代码,用Jython就不行了。

目前最新版本的Jython是2.7.3,该版本的说明如下:

The current version of Jython is 2.7.3. It can be downloaded here:

Jython Installer: Use this to install Jython. (metadata)
Jython Standalone: Use this to run Jython without installing or to embed Jython in a Java application. (metadata)
You may cite Jython 2.7.3 as a dependency in your Maven or Gradle build.
For information on installing see Installation.

This version is supported on Java 8 (minimum) and 11.

可以看到,Jython 2.7.3支持Java 8和11,如果使用更高版本的Java,可能会出现问题。我在Java 17环境中使用Jython 2.7.3就报错,按网上说的解决方法试了以后也没用。

所以Jython(2.7.3)最好是用在Java 8或11环境中,调用Python 2.x的代码。

Java版本与Jython包
  • 本文代码在Java 1.8.0_101下运行

  • 官网提供的包有jython-standalone和jython-slim,本文代码依赖其中任一均可

代码

1.在pom.xml中添加Jython相关依赖

        <!-- jython-standalone -->
        <dependency>
            <groupId>org.python</groupId>
            <artifactId>jython-standalone</artifactId>
            <version>2.7.3</version>
        </dependency>

        <!-- jython-slim -->
        <dependency>
            <groupId>org.python</groupId>
            <artifactId>jython-slim</artifactId>
            <version>2.7.3</version>
        </dependency>

2.调用Python代码

package com.example.study.common;

import org.python.core.PyFunction;
import org.python.core.PyInteger;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.util.PythonInterpreter;

public class JythonDemo {
    public static void main(String[] args) {
        String pythonFile = "F:\\tmp\\Hello.py";
        PythonInterpreter interpreter = new PythonInterpreter();
        execCode(interpreter);
        execFile(interpreter, pythonFile);
        execSpecifyFunction(interpreter, pythonFile);
    }

    private static void execSpecifyFunction(PythonInterpreter interpreter, String pythonFile) {
        interpreter.execfile(pythonFile);
        // ----------获取变量----------
        System.out.println("获取变量");
        // 第一个参数为期望获得的函数(变量)的名字,第二个参数为指定的函数(变量)的类型,jython的类型都是以Py开头
        PyString msg = interpreter.get("msg", PyString.class);
        System.out.println("获取到参数msg的值为:{}" + msg);
        System.out.println();

        // ----------获取并执行返回值为String的函数----------
        System.out.println("获取并执行返回值为String的函数");
        PyFunction hello = interpreter.get("hello", PyFunction.class);
        // 设置函数入参,并执行函数
        PyObject result = hello.__call__(new PyString("java_t_t"), new PyInteger(2));
        System.out.println("函数返回值:" + result);
        System.out.println("函数返回值类型:" + result.getType().getName());
        System.out.println();

        // ----------获取并执行返回值为int的函数----------
        System.out.println("获取并执行返回值为int的函数");
        PyFunction add = interpreter.get("add", PyFunction.class);
        result = add.__call__(new PyInteger(1), new PyInteger(2));
        System.out.println("函数返回值:" + result);
        System.out.println("函数返回值类型:" + result.getType().getName());
        System.out.println();

        // ----------获取并执行没有返回值的函数----------
        System.out.println("获取并执行没有返回值的函数");
        PyFunction hasNoReturn = interpreter.get("hasNoReturn", PyFunction.class);
        result = hasNoReturn.__call__();
        System.out.println("函数返回值:" + result);
        System.out.println("函数返回值类型:" + result.getType().getName());
        System.out.println();
    }

    /**
     * 执行python文件
     *
     * @param interpreter python代码执行器
     * @param pythonFile  python文件
     */
    private static void execFile(PythonInterpreter interpreter, String pythonFile) {
        System.out.println("执行python脚本时不传入参数:");
        interpreter.execfile(pythonFile);
        System.out.println();
        System.out.println("执行python脚本时传入参数:");
        interpreter.exec("import sys");
        interpreter.exec("sys.argv = ['arg1', 'arg2', 3, True, 'arg5']");
        interpreter.execfile(pythonFile);
        // 此处设置的sys.argv在后面会一直生效,需要清除
        interpreter.exec("sys.argv = []");
        System.out.println();
    }

    /**
     * 直接执行python代码
     *
     * @param interpreter python代码执行器
     */
    private static void execCode(PythonInterpreter interpreter) {
        System.out.println("直接执行python代码:");
        interpreter.exec("a = 1");
        interpreter.exec("b = 2");
        interpreter.exec("if a / b == 0:\n" +
                "    print('current jython version support python 2.x')\n" +
                "elif a / b == 0.5:\n" +
                "    print('current jython version support python 3.x')");
        interpreter.exec("import math");
        interpreter.set("num", 2);
        interpreter.exec("print('sqrt(' + str(num) + ') is:' + str(math.sqrt(num)))");
        System.out.println();
    }
}

Python代码文件Hello.py内容如下

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import math


def add(a, b):
    return a + b


def hello(name, repeat):
    return ('hello ' + name + '! ') * repeat


def hasNoReturn():
    pass


if __name__ == '__main__':
    import sys

    for index, value in enumerate(sys.argv):
        print('arg' + str(index) + ': ' + str(value))
    print('exec "__main__" function')

msg = '--finished--'
print(msg)

执行结果

直接执行python代码:
current jython version support python 2.x
sqrt(2) is:1.41421356237

执行python脚本时不传入参数:
arg0: 
exec "__main__" function
--finished--

执行python脚本时传入参数:
arg0: arg1
arg1: arg2
arg2: 3
arg3: True
arg4: arg5
exec "__main__" function
--finished--

exec "__main__" function
--finished--
获取变量
获取到参数msg的值为:{}--finished--

获取并执行返回值为String的函数
函数返回值:hello java_t_t! hello java_t_t! 
函数返回值类型:str

获取并执行返回值为int的函数
函数返回值:3
函数返回值类型:int

获取并执行没有返回值的函数
函数返回值:None
函数返回值类型:NoneType

使用ProcessBuilder执行python代码

ProcessBuilder说明

ProcessBuilder是Java提供的用于执行脚本的类,并不局限于执行Python脚本。所以除了提供ProcessBuilder执行Python脚本的例子外,也提供ProcessBuilder直接执行cmd命令和调用bat脚本的例子。

代码

1.调用脚本&直接执行cmd命令

package com.example.study.common;

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

@Slf4j
public class ProcessBuilderDemo {
    public static void main(String[] args) throws IOException {
        execCmd();
        execBatFile();
        execPythonFile();
    }

    private static void execPythonFile() throws IOException {
        System.out.println("执行Python脚本");
        String pythonEnvPath = "C:\\user\\software\\study\\python\\Python37\\python.exe";
        String pythonFile = "F:\\tmp\\Hello.py";
        // 参数说明:第一个参数是执行器,这里是python的路径,若该路径已添加到环境变量,也可以直接写"python",
        // 第二个参数是python脚本路径,第三个及后面的参数是sys.argv的入参。所有参数都是String类型
        ProcessBuilder processBuilder = new ProcessBuilder(pythonEnvPath, pythonFile, "arg1", "arg2", "arg3");
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        System.out.println(inputStreamToString(process.getInputStream()));
    }

    private static void execBatFile() throws IOException {
        System.out.println("执行bat脚本");
        ProcessBuilder processBuilder = new ProcessBuilder("C:\\Windows\\System32\\cmd.exe",
                "/C", "F:\\tmp\\PingLocalhost.bat", "arg1");
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        System.out.println(inputStreamToString(process.getInputStream()));
        System.out.println();
    }

    private static void execCmd() throws IOException {
        System.out.println("直接执行命令");
        ProcessBuilder processBuilder = new ProcessBuilder("C:\\Windows\\System32\\cmd.exe", "/C", "echo aaa");
        processBuilder.redirectErrorStream(true);
        Process process = processBuilder.start();
        System.out.println(inputStreamToString(process.getInputStream()));
        System.out.println();
    }

    private static String inputStreamToString(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] tmp = new byte[1024];
        int len;
        while ((len = inputStream.read(tmp)) != -1) {
            outputStream.write(tmp, 0, len);
        }
        inputStream.close();
        outputStream.close();
        return outputStream.toString();
    }
}

bat脚本文件PingLocalhost.bat内容如下


chcp 65001
echo first arg is: %0
echo second arg is: %1
ping 127.0.0.1

Python代码文件Hello.py内容与Java版本与Jython包章节的Hello.py一致

执行结果

直接执行命令
aaa


执行bat脚本

F:\code\java\study>chcp 65001 
Active code page: 65001

F:\code\java\study>echo first arg is: F:\tmp\PingLocalhost.bat 
first arg is: F:\tmp\PingLocalhost.bat

F:\code\java\study>echo second arg is: arg1 
second arg is: arg1

F:\code\java\study>ping 127.0.0.1 

Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=64
Reply from 127.0.0.1: bytes=32 time<1ms TTL=64
Reply from 127.0.0.1: bytes=32 time<1ms TTL=64
Reply from 127.0.0.1: bytes=32 time<1ms TTL=64

Ping statistics for 127.0.0.1:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms


执行Python脚本
arg0: F:\tmp\Hello.py
arg1: arg1
arg2: arg2
arg3: arg3
exec "__main__" function
--finished--
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值