目前系统接到一个需求,希望平台能支持运行python脚本,而我们的平台是由java编写的,所有我们
需要预言出我们最终的实现方案
经过调研,目前java调用python脚本主要有以下几种方式
Jython是Python编程语言的JVM实现。 它旨在在Java平台上运行。 Jython程序可以导入和使用任何Java类。 就像Java一样,Jython程序编译为bytecode 。 其中一个主要优点是用Python设计的用户界面可以使用AWT , Swing或SWT Package GUI元素。
Jython以JPython开头,后来被重命名,紧跟着Guido Van Rossum创建的标准Python实现CPython 。 Jython由Jim Hugunin于1997年创立。 Jython 2.0于1999年发布。从那时起,Jython 2.x版本对应于等效的CPython版本。 2015年5月发布的Jython 2.7.0对应于CPython 2.7。 Jython 3.x的开发正在进行中。
使用方式如下:
先引入jar包
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.0</version>
</dependency>
Map<String, Object> binding = new HashMap<>();
binding.put("name", "zhangshan");
binding.put("age", "18");
try (PythonInterpreter interpreter = new PythonInterpreter(Py.java2py(binding))) {
interpreter.exec("varMap = globals()");
interpreter.exec("varMap.put('name', 'ls')");
interpreter.exec("varMap.put('sex', '1')");
PyObject vars = interpreter.get("varMap");
System.out.println(vars.toString());
}
这种方式的好处是:
缺点也很明显:
java.lang.Runtime.exec(String command, String[] envp, File dir) 方法在指定环境和工作目录的独立进程中执行指定的字符串命令。这是一个方便的方法。exec(command, envp, dir)调用行为完全相同于调用 exec(cmdarray, envp, dir),其中cmdarray是命令的所有标记的数组。
更确切地说,该命令串被分成使用由调用new StringTokenizer(command) 与字符的类别没有进一步的修改创建StringTokenizer令牌。由标记生成器生成的令牌,然后以相同的顺序放置在新的字符串数组cmdarray。
使用方式如下:
首先先在宿主机安装python环境
int a = 18;
int b = 23;
try {
String[] args1 = new String[] { "python", "/Users/rayduan/PycharmProjects/pythonProject/test.py", String.valueOf(a), String.valueOf(b) };
Process proc = Runtime.getRuntime().exec(args1);// 执行py文件
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
proc.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
这种方式的好处显而易见:
但是缺点也不少:
if __name__ == '__main__':
print(sys.path)
#!/usr/local/bin/python3.7
import time
import os
count = 0
str = ('python b.py')
result1 = os.system(str)
print(result1)
while True:
count = count + 1
if count == 8:
print('this count is:',count)
break
else:
time.sleep(1)
print('this count is:',count)
print('Good Bye')
os.system()方法在子shell中执行命令(字符串)。该方法是通过调用标准C函数system()来实现的,并且具有相同的限制。如果命令生成任何输出,则将其发送到解释器标准输出流。无论何时使用此方法,都将打开操作系统的相应 shell 并在其上执行命令.遗憾的是它并没有返回值。
python另外有通过shell命令行调用python文件的方式,流程和java的Runtime.exec类似。
Jep使用JNI和CPython API来启动JVM中的Python解释器。当你在Java中创建一个Interpreter实例时,将为该Java Interpreter实例创建一个Python解释器,并保留在内存中,直到用Interpreter.close()关闭该Interpreter实例。由于需要管理一致的Python线程状态,创建Interpreter实例的线程必须在对该Interpreter实例的所有方法调用中重复使用。Jep应该与任何纯Python模块一起工作。它可以与各种CPython扩展一起工作。开发人员报告说,Jep可以与NumPy、Scipy、Pandas、TensorFlow、Matplotlib、cvxpy等一起工作。
使用方式如下:
首先我们需要python运行环境,jep能很好的支持python2.0以及3.0,这里我们以3.8为例(jep目前最高支持python3.8,框架还在持续更新中)。
pip3 install jep
<dependency>
<groupId>black.ninia</groupId>
<artifactId>jep</artifactId>
<version>4.0.3</version>
</dependency
通过上述配置后我们就搭建好了jep的运行环境,使用起来也很简单
/**
* 从python中拿到变量值
* @param scriptContent
*/
public void runScriptWithReturn() {
try (SharedInterpreter interpreter = new SharedInterpreter()) {
interpreter.exec("import sys");
Object value = interpreter.getValue("sys.path");
System.out.println(value);
}
}
/**
* 调用python文件中的方法
*/
public void runScriptWithFileMethod() {
JepConfig jepConfig = new JepConfig().addIncludePaths("/Users/rayduan/PycharmProjects/pythonProject");
SharedInterpreter.setConfig(jepConfig);
try (Interpreter interpreter = new SharedInterpreter()) {
interpreter.exec("from demo import *");
Object result = interpreter.invoke("func", 1, 2);
System.out.println(result);
}
}
/**
* 通过python脚本修改变量中的值
*/
public void runScriptWithChangeParam() {
try (Interpreter interpreter = new SharedInterpreter()) {
Map<String, Object> binding = new HashMap<>();
binding.put("name", "zhangshan");
binding.put("age", "18");
interpreter.set("vars", binding);
interpreter.exec("vars['name'] = 'ls'");
Map vars = interpreter.getValue("vars", Map.class);
System.out.println(vars);
}
}
根据我们需求,jep能完全覆盖我们的使用场景,下面我们说下jep的优缺点。
优点:
缺点:
总结:
目前存在的需求是支持python3.0,同时我们的目前平台脚本功能主要是通过脚本去改变输入输出的参数或者场景运行时参数。综合上述观点,jep无疑是最好的选择