骚操作 !Java 中调用 Python,动态传入参数 !

小哈学Java

共 4952字,需浏览 10分钟

 ·

2022-04-01 21:13

点击关注公众号,Java干货及时送达👇

f41945c13e3cb0acfcfcd0b94f0542b6.webp

目录

  • 一、前言
  • 二、Jython 官网地址
  • 三、上手试试
    • 添加依赖
    • 测试执行 Python
    • 在 Java 类中直接执行 Python 语句
    • 在 Java 中调用 Python 脚本文件
    • 另外一种方式:Runtime.getRuntime() 执行 python 脚本文件
    • 调用 Python 脚本发送邮件,并动态传入参数,打印结果
  • 四、结语

一、前言

大家好,我是小哈~

今天给大家分享一下如何在 Java 中执行 Python 脚本,这种需求,小哈在以前参与的定时任务调度平台就遇到过,需要定时去触发某个作业,作业需要支撑业务线丰富的场景,可以是接口调用、Shell 脚本执行、推送 MQ、Python 脚本执行等。

除此以外,大家都知道 Python 拥有非常丰富的第三方类库,有些功能用 Python 来实现的话,可能仅仅需要引入一下包,几行代码就搞定了,用 Java 来实现可能就需要费老鼻子劲了。

好了,废话也不多说,进入今天的正题 !

给大家安利一个优秀的第三方库 Jython, 通过它可以非常方便的执行 Python 脚本。

二、Jython 官网地址

官网地址:https://www.jython.org/

Github 地址:https://github.com/jython/jython

三、上手试试

小哈为了给大家演示,先新建一个 Spring Boot 项目。

3.1 添加依赖

在 maven 的 pom.xml 中添加 Jython 依赖:


org.python
jython-standalone
2.7.2

3.2 测试执行 Python

注意,需要在你的机器上先安装好 Python 环境。

3.2.1 在 Java 类中直接执行 Python 语句

新建单元测试,通过 Java 代码直接执行 Python 语句:

@Test
void executePython() {
    // 一条简单的 python 语句,打印 Hello world !
    String py = "print('Hello world!')";

    PythonInterpreter interpreter = new PythonInterpreter();
    // 执行 Python 语句
    interpreter.exec(py);
}

输出:

58682bdce5809e913db00d07e87dd2f1.webp通过Jython执行一条简单的python语句

3.2.2 在 Java 中调用 Python 脚本文件

大多数情况下,需要执行的是具体目录下的 Python 脚本,为了测试,我们在 D 盘的根目录下新建 test.py,将刚刚的 Hello world 代码以文件的形式来运行:

d98684ac5506ce7fa20cb1e5b326369a.webppython输出hello world脚本

通过 Jython 去执行这个脚本:

@Test
void executePythonFile() {
    // 定义脚本路径
    String path = "D:\\test.py";

    PythonInterpreter interpreter = new PythonInterpreter();
    // 执行脚本文件
    interpreter.execfile(path);
}

可以看到,执行 Python 脚本也是 ok 的:

ccd6d9beed692b18c3e0866146815d02.webp在java中执行python脚本

3.2.3 另外一种方式:Runtime.getRuntime() 执行 python 脚本文件

还是刚刚的测试脚本,还可以通过  Runtime.getRuntime() 来执行, 示例代码如下:

@Test
void executePythonFile2() {
    Process proc;
    BufferedReader reader;
    try {
        // 直接执行 python 命令的方式来执行脚本
        proc = Runtime.getRuntime().exec("python D:\\test.py");
        reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));

        String line = null;
        while ((line = reader.readLine()) != null) {
            // 打印输出
            System.out.println(line);
        }

        reader.close();
        proc.waitFor();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

输出结果同上。

3.2.4 调用 Python 脚本调用某个函数,并动态传入参数,打印结果

实际的业务场景中,我们可能需要在调用脚本文件时动态传入参数,并打印结果。这里为了测试,小哈简单写了个发送邮件的 Python 脚本,定义了一个 sendEmail 函数,需要传入以下 3 个参数:

  • 邮件接受者:receiver
  • 邮件标题:subject
  • 邮件内容:content

代码如下:

import smtplib
from email.mime.text import MIMEText

# 邮件服务器地址
mail_host = 'smtp.qq.com'
# 邮件服务器用户名
mail_user = 'xxx@qq.com'
# 邮件服务器登录密码(有些是授权码)
mail_pwd = 'xxx'
# 邮件发送者
sender = 'xxx@qq.com'

def sendEmail(receiver, subject, content):
    """
    发送邮件
    :param receiver: 接收者
    :param subject: 邮件主题
    :param content: 邮件内容
    :return:
    """

    message = MIMEText(content, 'plain''utf-8')
    # 邮件主题
    message['Subject'] = subject
    # 发送方
    message['From'] = sender
    # 接收方
    message['To'] = receiver

    try:
        # 连接到服务器
        smtp = smtplib.SMTP_SSL(mail_host)
        # 登录服务器
        smtp.login(mail_user, mail_pwd)
        # 发送
        smtp.sendmail(sender, receiver, message.as_string())
        # 推出
        smtp.quit()
        return 'send email success ...'
    except smtplib.SMTPException as e:
        print('error', e)


if __name__ == '__main__':
    # 邮件接受者
    receiver = 'xxx@qq.com'
    # 邮件主题
    subject = '打个招呼'
    # 邮件内容
    content = '我是犬小哈'
    sendEmail(receiver, subject, content)

代码中使用的是 QQ 邮件,邮件服务器的用户名和授权码就不放出来了,有兴趣的小伙伴,可将其替换自己的试试。

再来看看如何通过 Jython 去调用 sendEmail 函数:

@Test
void executePythonFunction() {
    PythonInterpreter interpreter = new PythonInterpreter();
    // 指定指定路径下的 python 脚本
    interpreter.execfile("D:\\test.py");

    // 指定需要调用的函数
    PyFunction function = interpreter.get("sendEmail", PyFunction.class);
    // ↓↓↓↓↓↓↓↓ 需要传入的参数 ↓↓↓↓↓↓↓↓
    // 邮件接受者
    PyString receiver = Py.newStringOrUnicode("xxx@qq.com");
    // 邮件主题
    PyString subject = Py.newStringOrUnicode("打个招呼【动态参数】");
    // 邮件内容
    PyString content = Py.newStringOrUnicode("我是犬小哈【动态参数】");

    // 调用
    PyObject pyObject = function.__call__(receiver, subject, content);

    // 打印返回结果
    System.out.println(String.format("result: %s", pyObject));
}

执行一下,你会发现报错:

f00da06bbf39bb34275f4b46fb5b2329.webpjython调用python脚本的函数

Python 脚本在没有指定具体编码时,默认以 ASCII 编码,这里因为文件中涉及到中文,需要指定编码格式为 utf-8, 在脚本文件头添加如下代码:

# -*- coding: utf-8 -*-
11040cdef0e513ad6e17b47f6aacf555.webp在python文件头部指定编码格式

PS: 详细参考:https://peps.python.org/pep-0263/

再次执行看看:

81e1998ee7665821923ba84472a178ef.webppython发送邮件结果

成功打印了返回参数,确认一下是否真的收到邮件了:

6d15352d750a10e8ddbddcead8644d9a.webp邮件接受成功

成功收到了邮件。

细心的小伙伴应该发现了,在控制台打印中有个 500 错误,提示内容被拒绝了,但确实又收到了邮件,这是个啥情况,点进后面的连接看了一下 http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=20022&&no=1000726

f6e176954b07c926628ef545064b3553.webp提示垃圾邮件

初步怀疑是自己做测试发送了过多的邮件导致,被 QQ 判定为垃圾营销邮件了,但是收还是能收到的,小伙伴们如果收不到也可以将发送者的邮箱地址设置为 QQ 邮件白名单。

四、结语

小伙伴们如果有需要在 Java 中执行 Python 脚本的,不妨试试 Jython 这个库,整体来说还是非常棒滴~ 这期分享就到这里了,下期见~

文章首发自个站:犬小哈教程

https://www.quanxiaoha.com/article/java-invoke-python-by-jython.html

五、Ref

  • https://www.cnblogs.com/wuwuyong/p/10600749.html

  • https://blog.csdn.net/qq_26591517/article/details/80441540

1. HashMap夺命14问,你能坚持到第几问?

2. 如何保证数据库和缓存双写一致性?

3. 3条原则,16个小点,帮你写出一个优秀的对外接口!

4. Nginx 轻松搞定跨域问题 !

最近面试BAT,整理一份面试资料Java面试BATJ通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。

PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下在看,加个星标,这样每次新文章推送才会第一时间出现在你的订阅列表里。

“在看”支持小哈呀,谢谢啦😀

浏览 105
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报