从 Python 调用 Java
Posted
技术标签:
【中文标题】从 Python 调用 Java【英文标题】:Calling Java from Python 【发布时间】:2011-04-08 19:53:53 【问题描述】:从 python 调用 java 的最佳方法是什么? (jython 和 RPC 不适合我)。
我听说过 JCC:http://pypi.python.org/pypi/JCC/1.9 用于从 C++/Python 调用 Java 的 C++ 代码生成器 但这需要编译所有可能的调用;我更喜欢另一种解决方案。
我听说过 JPype:http://jpype.sourceforge.net/ 教程:http://www.slideshare.net/onyame/mixing-python-and-java
import jpype
jpype.startJVM(path to jvm.dll, "-ea")
javaPackage = jpype.JPackage("JavaPackageName")
javaClass = javaPackage.JavaClassName
javaObject = javaClass()
javaObject.JavaMethodName()
jpype.shutdownJVM()
这看起来像我需要的。 但是,最后一个版本是从 2009 年 1 月开始的,我看到有人无法编译 JPype。
JPype 是一个死项目吗?
还有其他选择吗?
【问题讨论】:
您能否详细说明为什么您认为 Jython 和 RPC 不适合您的情况? 看起来在此期间有一个新的 JPype 版本:2011-07-28 上的 0.5.4.2 这是一个边缘题外的问题,部分要求软件推荐,部分还不够清楚(“最佳方式”的确切要求是什么)。也许即使在今天,这个问题仍然可以改进。 【参考方案1】:您也可以使用Py4J。头版上有一个示例和大量文档,但本质上,您只需从 Python 代码中调用 Java 方法,就好像它们是 Python 方法一样:
from py4j.java_gateway import JavaGateway
gateway = JavaGateway() # connect to the JVM
java_object = gateway.jvm.mypackage.MyClass() # invoke constructor
other_object = java_object.doThat()
other_object.doThis(1,'abc')
gateway.jvm.java.lang.System.out.println('Hello World!') # call a static method
与 Jython 不同,Py4J 的一部分在 Python VM 中运行,因此它始终与最新版本的 Python “保持同步”,您可以使用在 Jython 上运行不佳的库(例如 lxml)。另一部分在您要调用的 Java VM 中运行。
通信是通过套接字而不是 JNI 完成的,Py4J 有自己的协议(优化某些情况,管理内存等)
免责声明:我是 Py4J 的作者
【讨论】:
感谢您的链接。它看起来像是 djna 提出的 CodeMesh 的开源替代方案。我会明确地看看它。但是和CodeMesh有同样的问题,需要先启动Java进程,并确保在使用python之前已经运行(参见项目主网页中的示例,ListPrinter.java -> main -> GatewayServer.start( ))。这是一个可能的故障点。我仍然认为 JPype 的方法非常好;只是它似乎是一个死项目。 @alvas 如果这就是你的意思,我仍然维护 Py4J。 @Barthelemy,如果 Java 代码依赖于库 - 在我的情况下是 opencv,如何进行集成? @stack 只需确保将 opencv 添加到您的类路径中,您就可以在启动 GatewayServer 时从 Python 访问它。 这适用于任何软件包吗?我试过:s = gateway.jvm.ch.ethz.ssh2.crypto.Base64() bt_out = s.decode();
这里的 Base64 类有方法 encode() 和 decode() 并且是我的 .jar 文件中包 ch.ethz.ssh2.crypto
的一部分。我得到from py4j.reflection import MethodInvoker ImportError: No module named reflection
【参考方案2】:
这是我对这个问题的总结:5 种从 Python 调用 Java 的方法
http://baojie.org/blog/2014/06/16/call-java-from-python/(缓存)
简短回答:Jpype 工作得很好,并且在许多项目中得到了证明(例如 python-boilerpipe),但是 Pyjnius 比 JPype 更快更简单
我尝试过 Pyjnius/Jnius、JCC、javabridge、Jpype 和 Py4j。
Py4j 有点难用,因为你需要启动一个网关,增加了另一层脆弱性。
【讨论】:
【参考方案3】:Pyjnius docs 和 Github。
来自 github 页面:
使用 JNI 将 Java 类作为 Python 类访问的 Python 模块。
PyJNIus 是“正在进行中的工作”。
快速概览
>>> from jnius import autoclass >>> autoclass('java.lang.System').out.println('Hello world') Hello world >>> Stack = autoclass('java.util.Stack') >>> stack = Stack() >>> stack.push('hello') >>> stack.push('world') >>> print stack.pop() world >>> print stack.pop() hello
【讨论】:
【参考方案4】:我在 OSX 10.10.2 上,并成功使用了 JPype。
在使用 Jnius (others have too) 时遇到安装问题,Javabridge 已安装,但在我尝试使用时出现了神秘错误,PyJ4 的不便之处在于必须先在 Java 中启动网关服务器,JCC 无法安装。最后,JPype 最终工作了。 Github 上有一个maintained fork of JPype。它的主要优点是 (a) 可以正确安装 (b) 它可以非常有效地将 java 数组转换为 numpy 数组 (np_arr = java_arr[:]
)
安装过程是:
git clone https://github.com/originell/jpype.git
cd jpype
python setup.py install
你应该可以import jpype
以下演示有效:
import jpype as jp
jp.startJVM(jp.getDefaultJVMPath(), "-ea")
jp.java.lang.System.out.println("hello world")
jp.shutdownJVM()
当我尝试调用我自己的 java 代码时,我必须先编译 (javac ./blah/HelloWorldJPype.java
),并且我必须更改默认的 JVM 路径(否则你会得到莫名其妙的“找不到类”错误)。对我来说,这意味着将 startJVM 命令更改为:
jp.startJVM('/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/MacOS/libjli.dylib', "-ea")
c = jp.JClass('blah.HelloWorldJPype')
# Where my java class file is in ./blah/HelloWorldJPype.class
...
【讨论】:
一个使 JPype 更易于使用的小包装模块在这里:github.com/petered/spiking-mlp/blob/master/spiking_mlp/…【参考方案5】:如果您使用的是 Python 3,则有一个名为 JPype1-py3 的 JPype 分支
pip install JPype1-py3
这适用于我在 OSX / Python 3.4.3 上。 (您可能需要export JAVA_HOME=/Library/Java/JavaVirtualMachines/your-java-version
)
from jpype import *
startJVM(getDefaultJVMPath(), "-ea")
java.lang.System.out.println("hello world")
shutdownJVM()
【讨论】:
【参考方案6】:我最近将很多东西集成到 Python 中,包括 Java。我发现的最可靠的方法是使用 IKVM 和 C# 包装器。
IKVM 有一个简洁的小应用程序,允许您获取任何 Java JAR,并将其直接转换为 .Net DLL。它只是将 JVM 字节码转换为 CLR 字节码。详情请见http://sourceforge.net/p/ikvm/wiki/Ikvmc/。
转换后的库的行为就像原生 C# 库一样,您可以在不需要 JVM 的情况下使用它。然后,您可以创建一个 C# DLL 包装项目,并添加对转换后的 DLL 的引用。
您现在可以创建一些包装存根来调用您想要公开的方法,并将这些方法标记为 DllEport。详情请见https://***.com/a/29854281/1977538。
包装 DLL 的行为就像原生 C 库一样,导出的方法看起来就像导出的 C 方法。您可以像往常一样使用 ctype 连接到它们。
我已经在 Python 2.7 上尝试过,但它也应该适用于 3.0。适用于 Windows 和 Linux
如果您碰巧使用 C#,那么这可能是在将几乎所有内容集成到 python 时尝试的最佳方法。
【讨论】:
呃...你在 C# 中失去了我。我不会投反对票,因为这在某些情况下是可行的,但这肯定是假设 Windows 和许多其他东西。【参考方案7】:我刚刚开始使用 JPype 0.5.4.2(2011 年 7 月),它看起来运行良好... 我在 Xubuntu 10.04
【讨论】:
【参考方案8】:我假设如果您可以从 C++ 转换到 Java,那么您已经准备就绪。我见过你提到的那种产品效果很好。碰巧我们使用的是CodeMesh。我没有特别认可这家供应商,也没有就他们产品的相对质量发表任何声明,但我已经看到它在相当大批量的情况下工作。
我一般会说,如果可能的话,我建议尽量避免通过 JNI 直接集成。一些简单的 REST 服务方法或基于队列的架构往往更易于开发和诊断。如果你仔细使用这种解耦技术,你可以获得相当不错的性能。
【讨论】:
RPC(或 REST)不适合我。 这需要先启动Java进程,并在使用python之前确保它正在运行。这是一个可能的故障点。 JPype 的方法非常好;只是它似乎是一个死项目。 我提供一般建议。 JNI 是一个潜在的雷区。【参考方案9】:根据我自己尝试在 python 中以类似于 python 代码在 python 中的 java 代码中运行的方式运行一些 java 代码的经验,我无法找到一种简单的方法。
我对我的问题的解决方案是,在使用适当的包和变量编辑临时文件中的 java 代码之后,通过在我的 python 代码中将 BeanShell 解释器作为 shell 命令调用,将这个 java 代码作为 BeanShell 脚本运行。
如果我所谈论的内容对您有任何帮助,我很高兴能帮助您分享我的解决方案的更多细节。
【讨论】:
以上是关于从 Python 调用 Java的主要内容,如果未能解决你的问题,请参考以下文章