由于工作需要 最近一直在找如何在Java应用中调用CPython的深度学习库

GraalVM

官网: https://www.graalvm.org/

GitHub: https://github.com/oracle/graal

GraalVM是Oracle基于OpenJDK开发的全新VM, 但是GraalVM的定位是一个平台, 而不只是一个新的OpenJDK
能兼容JVM语言、Node、Js、Ruby、R、Python、C/C++ LLVM等大部分主流语言
由于Windows还在Beta阶段, 所以只能在Linux下使用跨语言特性
这里主要用到的Java调用Python

GraalVM下通过Polyglot来进行语言之间的交互

import org.graalvm.polyglot.*;

class Polyglot {
    public static void main(String[] args) {
        Context context = Context.newBuilder().allowIO(true).build();
        Value array = context.eval("python", "[1,2,42,4]");
        int result = array.getArrayElement(2).asInt();
        System.out.println(result);
    }
}
$ javac Polyglot.java
$ java Polyglot
42

的确通过GraalVM能在Java下调用类似原生的Python了, 为什么说是类似原生呢
经过了解GraalVM下的所有语言都是使用 Truffle 来重新实现的, 相当于用Java重新写了一遍,
然后用GraalVM编译成LLVM来调用Native层

GraalPython: https://github.com/graalvm/graalpython

带来的好处不用我说, 但是目前也存在很多问题
由于Python部分还是测试中, 所以说很多Python的原生模块还没实现
比如Zlib、SSL模块
在我自己的测试中 GraalPython不能使用PIP安装库, 就是因为模块还不完整
所以说只能等待官方完善后再往后细探

JNA/JNI

Java调用C/C++使用的是JNI
而JNA是JNI的封装, 使其更加易于使用

尝试使用编译Python的动态链接库
我对C/C++也不太懂 Python也是一个庞大的体系 为了方便我使用了JNAerator来生产JNA类

JNAerator: https://github.com/nativelibs4java/JNAerator

跟着JNAerator的WIKI在Linux环境下生产出来了libpython36m.so的JNA类
的确通过JNA调用成功了, 但是出现了一些问题, 这里就不给调用例子了

不能传参, 只能通过eval调用, 应该是这个生成器生成的原因

就算可以传参, 调用起来也比较繁琐, 得通过Jna调用C来生产Python对象再调用, 所以说不推荐这个方法

Jep: 通过JNI将CPython嵌入到Java中

Jep Github: https://github.com/ninia/jep

Jep需要:

  1. JDK8+
  2. Python
  3. Microsoft Visual C++ 2015 Build Tools [5G大小强装C盘警告]

在安装完JDK8后需要配置JAVA_HOME和PATH环境变量
Python和MSVC2015则需要PATH, 怎么配置这就不赘述, 安装程序也提供了PATH自动配置

接下来就是直接Clone Jep然后编译安装就行了

$ git clone https://github.com/ninia/jep.git
$ cd jep
$ python setup.py build
$ python setup.py install

不出意外Jep就已经编译并安装好了

在Python安装目录/Lib/site_packages/jep下找到 jep.dll 和 jep-版本号.jar 复制到项目文件夹内 创建测试类Main

import jep.Interpreter;
import jep.JepException;
import jep.SharedInterpreter;

public class Main {
    public static void main(String[] args) {
        try (Interpreter interp = new SharedInterpreter()) {
            interp.exec("from java.lang import System");
            interp.exec("s = 'Hello World'");
            interp.exec("System.out.println(s)");
            interp.exec("print(s)");
            interp.exec("print(s[1:-1])");
            interp.exec("import platform" );
            interp.exec("System.out.println(platform.python_version())" );
        } catch (JepException e) {
            e.printStackTrace();
        }
    }
}
$ javac Main.java
$ java Main
Hello World
3.6.8

运行可以看到不仅能通过java调用python, python也能调用java的类, 能获取到具体的返回值, 用起来也不算太复杂

所以说现阶段我推荐使用Jep, 以后GraalVM肯定是主流, 等Oracle完善后再向GraalVM迁移

标签: Java, Python, GraalVM