最近在做一个研究,因为需要使用机器学习的方法而使用了python,而模型的验证我选择了一个开源库,但是这个开源库是采用java编写的。因此需要在验证的时候使用java调用python才行。
通过查找发现,java调用python其实并不困难,网上有很多方法实现。但是我的情况比较特殊,不能够简单地采取其中的任意一条,通过相关的搜索,我综合了几个方法完成了我的python调用器。
基本原理
采用 Runtime.getRuntime()
的方法运行python脚本,通过在python文件中调用 print()
函数来返回结果。
可能存在的问题
- 不是默认的python环境。找到对应环境的路径,修改命令的参数即可。
Runtime.getRuntime(“python”, path)
→Runtime.getRuntime(“xxx/xxx/python.exe”, path)
- 调用了其它文件夹下的包。这种情况下,直接采用命令台运行会报错。基本原理是针对python脚本做修改,在开头添加
sys.path.append(root)
这样一句即可(root 表示项目的根文件夹,加入这个可以调用该文件夹下所有包)。为了将这个修改在java程序中完成,我设计了一个缓存机制,先读取目标代码,在读取结果的开头加入import sys
和sys.path.append(root)
,并写入本地的缓存目录,实际调用的是这个新生成的文件,最后再删除缓存即可。
根据以上方法,我完成了一个简单的python执行器。以下为调用方法和实现方式。
调用方法
String pyPath = "test.py"; // python文件路径
String pyEnvironment = "python"; // 默认为python,如果使用了anaconda创建了环境,可以找到对应的路径并替换,类似于"E:\\Anaconda3\\envs\\xxx\\python.exe"。
PythonRun pythonRun = new PythonRun(); // 创建实例
pythonRun.setEnvironment(pyEnvironment); // 设置环境
pythonRun.setRoot("E:\\pythonProject\\xxx\\src"); // 设置python项目的执行目录,若不设置,在调用了其它包时,可能会出现错误。如果没有import其它文件夹下的包或库,可以忽略。
System.out.println(pythonRun.run(pyPath, "1", "2")); // 参数为:(String path, String ...args)
对应的python文件:
import sys
if __name__ == '__main__':
x = []
if len(sys.argv) > 1:
x = sys.argv[1:]
print(x)
print("over")
打印的结果:
['1', '2']
over
Process finished with exit code 0
实现
import java.io.*;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class PythonRun {
private String environment = "python";
private String root = null;
private String cache = "cache/";
private boolean autoRemoveCache = true;
public String run(String path, String ...args) throws IOException {
path = createNewPy(path);
List<String> inputArgs = new LinkedList<>(Arrays.asList(environment, path)); //设定命令行
inputArgs.addAll(Arrays.asList(args));
StringBuilder result = new StringBuilder();
try {
Process proc = Runtime.getRuntime().exec(inputArgs.toArray(new String[0])); //执行py文件
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result.append(line).append("\n");
}
in.close();
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
if (autoRemoveCache && path != null)
new File(path).delete();
return result.toString();
}
private String createNewPy(String path) throws IOException {
File file = new File(path);
if (file.isFile()){
String result = loadTxt(file);
if (root != null){
result = "import sys\n" +
"sys.path.append(\"" + root + "\")\n" + result;
}
String save = cache + file.getName();
saveTxt(save, result);
return save;
}
return null;
}
private static File saveTxt(String filename, String string){
File file = new File(filename);
try {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file),"UTF-8"));
out.write(string);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return file;
}
private String loadTxt(File file){
StringBuilder result = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
String str;
while ((str = in.readLine()) != null) {
result.append(str).append("\n");
}
}catch (Exception e){
e.printStackTrace();
}
return result.toString();
}
public String getCache() {
return cache;
}
public void setCache(String cache) {
this.cache = cache;
}
public String getEnvironment() {
return environment;
}
public void setEnvironment(String environment) {
this.environment = environment;
}
public String getRoot() {
return root;
}
public void setRoot(String root) {
this.root = root;
}
public boolean isAutoRemoveCache() {
return autoRemoveCache;
}
public void setAutoRemoveCache(boolean autoRemoveCache) {
this.autoRemoveCache = autoRemoveCache;
}
}