在Java中调用Python?

139

我想知道是否可以使用Jython从Java代码中调用Python函数,还是只能用于从Python中调用Java代码?


4
请查看类似的问题并阅读答案。http://bytes.com/topic/python/answers/22390-executing-jython-function-java - AlexR
13个回答

112

Jython: 适用于Java平台的Python - http://www.jython.org/index.html

使用Jython,您可以轻松地从Java代码中调用Python函数。只要您的Python代码本身在Jython下运行,即不使用某些不受支持的C扩展。

如果这对您有效,那么这绝对是您可以得到的最简单的解决方案。否则,您可以使用Java6解释器支持中的org.python.util.PythonInterpreter

以下是我能想到的一个简单示例-但希望能够工作:(为了简洁起见未进行错误检查)

PythonInterpreter interpreter = new PythonInterpreter();
interpreter.exec("import sys\nsys.path.append('pathToModules if they are not there by default')\nimport yourModule");
// execute a function that takes a string and returns a string
PyObject someFunc = interpreter.get("funcName");
PyObject result = someFunc.__call__(new PyString("Test!"));
String realResult = (String) result.__tojava__(String.class);

截至2021年,Jython不支持Python 3.x。


2
我没有遇到任何错误,只是在将Jython集成到Netbeans中遇到了困难。 - Shahab
@Voo 我将jython.jar作为外部库导入,但是出现了错误:找不到类'org.python.util.PythonInterpreter'。 - Lion.k
这个可以运行!但我有另一个问题,PyObject obj = interpreter.compile("..."); // 一段包含两个函数 myFunc1 和 myFunc2 的代码,如何调用指定的函数,例如 myFunc1 - soulmachine
4
Python 2的生命周期已于2020年1月1日结束,Jython仅支持Python 2.7版本,因此Jython基本上已经死亡。最好(也是最简单)的选择是Jep库。 - Flummiboy
2
Jython已经长时间处于休眠状态[ /死亡]。这太糟糕了。 - WestCoastProjects
显示剩余7条评论

73

我认为在你希望Java和Python之间的链接有多强大时,首先需要考虑一些重要的事情。

首先,你只想调用函数还是实际上希望Python代码改变Java对象中的数据?这非常重要。如果你只想调用一些带或不带参数的Python代码,那就不太困难。如果你的参数是基本类型,那就更容易了。然而,如果你希望Java类在Python中实现成员函数,并改变Java对象的数据,那么这就不那么容易或直接了。

其次,我们讨论的是CPython还是Jython?我会说CPython才是王道!我会主张这正是Python如此酷的原因!它具有如此高的抽象性,但在需要时可以访问C或C++。想象一下,如果你能在Java中做到这一点。如果Jython可以的话,这个问题甚至不值得提问,因为那样很容易。

所以我尝试了以下几种方法,并按照从简单到困难的顺序列出:

Java到Jython

优点:非常简单。可以实际引用Java对象。

缺点:没有CPython,非常慢!

Jython从Java转换非常简单,如果这已经足够了,那太好了。然而它非常慢,没有CPython!没有CPython的生活还有意义吗?我不这么认为!你可以轻松地使用Python代码来实现你的Java对象的成员函数。
通过Pyro将Java转换为Jython再转换为CPython
Pyro是Python的远程对象模块。您在CPython解释器上有一些对象,并且可以通过序列化发送对象,也可以通过此方法返回对象。请注意,如果您从Jython发送一个序列化的Python对象,然后调用一些函数来更改其成员中的数据,那么您将无法在Java中看到这些更改。您只需要记住从Pyro发送回您想要的数据即可。我相信,这是最简单的到达CPython的方法!您不需要任何JNI、JNA、SWIG等等。您不需要了解任何C或C++。酷吧?
优点: - 访问CPython - 不像其他方法那样困难
缺点:
  • 无法直接从Python直接更改Java对象的成员数据
  • 有点间接(Jython是中间人)

通过JNI/JNA/SWIG将Java转换为C/C++,再通过嵌入式解释器将其转换为Python(可能使用BOOST库?)

OMG这个方法可不是给胆小鬼用的。我可以告诉你,我花了很长时间才找到一个合适的方法来实现这个。你想要这样做的主要原因是为了能够运行CPython代码,让它完全控制你的Java对象。在决定尝试将Java(就像一只黑猩猩)和Python(就像一匹马)结合起来之前,有几个重要的事情需要考虑。首先,如果解释器崩溃了,你的程序就完蛋了!别让我再提并发问题了!此外,还有很多琐碎的东西,我相信我已经找到了最佳配置来最小化这些琐碎的东西,但仍然有很多!那么如何开始呢: 首先要知道C++是你的中间人,你的对象实际上是C++对象!很高兴你现在知道了这一点。只需将你的对象编写得好像你的程序是用C++而不是Java编写的,包括你想从两个世界访问的数据。然后,你可以使用名为SWIG的包装生成器,使其对Java可访问,并编译一个dll,在Java中调用该dll(System.load(dllNameHere))。首先确保这个工作正常,然后再进行下一步! 要进入Python,你需要嵌入一个解释器。首先,我建议你做一些简单的解释器程序或者参考这个教程Embedding Python in C/C。一旦你搞定了这个,就是让马和猴子跳舞的时候了!你可以通过[boost][3]将你的C++对象发送到Python中。我知道我没有给你鱼,只是告诉你在哪里找到鱼。在编译时,请注意以下几点。
当你编译boost时,你需要编译一个共享库。你需要包含并链接到你从jdk中需要的东西,比如jawt.lib、jvm.lib(在启动应用程序时,你还需要将client jvm.dll添加到路径中)。还需要包含python27.lib或其他版本的库以及boost_python-vc100-mt-1_55.lib。 然后包含Python/include、jdk/include和boost,并且只使用共享库(dll),否则boost会出问题。是的,我知道这很复杂。有很多方面可能会出错。所以确保逐个完成每个任务,然后将它们组合起来。

2
这里有一个库,让你只需编写一次 Python 脚本,就可以在运行时决定使用哪种集成方法(Jython、通过 Jep 的 CPython 和 Py4j):https://github.com/subes/invesdwin-context-python。由于每种方法都有其自身的优缺点,因此可以根据需要进行选择。 - subes
@subes 那个项目看起来很棒,我写了一段时间。我鼓励你写一个答案,我会点赞的。我不喜欢最高票的答案,因为我认为它除了谷歌搜索之外没有提供太多有用的信息。 - Derrops
我创建了一个单独的答案。 - subes
3
这个回答是否让人感觉像“这应该被采纳为正确答案”或者只有我这样觉得?;-) - Mauricio Gracia Gutierrez

28

将Python代码嵌入Java程序并不明智。使用Flask或其他Web框架将Python代码封装成微服务,这样您的Java程序就可以调用该微服务(例如通过REST)。

这种方法简单易行,能够避免大量问题的发生。而且代码松散耦合,易于扩展。

2020年3月24日更新: 根据@stx的评论,上述方法不适用于客户端和服务器之间的大量数据传输。 以下是我推荐的另一种方法:使用Rust(C/C++也可)连接Python和Java。 https://medium.com/@shmulikamar/https-medium-com-shmulikamar-connecting-python-and-java-with-rust-11c256a1dfb0


+1,但是创建并在两端解析来回消息的额外性能和通信开销怎么办? - stx
是的,这需要定义一个最小输入/输出要求的API接口。在网络上客户端/服务器之间传输大量数据并不明智。如果您无法定义这样的接口,则此设计方法不适用。 - Peiming Hu

12

有些回答提到可以使用JNI或JNA来访问cpython,但我不建议从头开始,因为已经有开源库可以从Java中访问cpython。例如:


11

GraalVM是一个很好的选择。我曾经使用GraalVM进行Java+Javascript的微服务设计(Java与Javascript反射)。他们最近增加了对Python的支持,尤其是考虑到它在过去几年中社区的规模已经变得非常大,我觉得可以试一试。

2021年6月更新:

https://www.graalvm.org/reference-manual/python/中提到:

GraalVM提供符合Python 3.8标准的运行环境。GraalVM Python运行时的主要目标是支持SciPy及其组成库,并且能够与来自Python生态系统的其他数据科学和机器学习库一起工作。当前阶段,Python运行时可供实验和好奇的终端用户使用。


7

这里有一个库,可以让你编写一次Python脚本,并在运行时决定使用哪种集成方法(通过 Jep 和 Py4j 的 CPython/PyPy、Jython):

https://github.com/subes/invesdwin-context-python

由于每种方法都有其自己的优缺点,如链接中所述。
(3个模块:jep、Py4J、jython)


该项目没有任何发布。 - Christian Schlichtherle
@ChristianSchlichtherle的invesdwin-context-python版本1.0.0不可用;感谢这个小宝石:https://github.com/loewenfels/dep-graph-releaser - subes
项目发布现已可用,请访问 https://github.com/invesdwin/invesdwin-context-python/issues/1 ,但是需要考虑此项目所带来的依赖数量,请参见 https://github.com/invesdwin/invesdwin-context-python/issues/4。 - Paul Verest

6

这取决于你所说的Python函数是什么?如果它们是用cpython编写的,你不能直接调用它们,你必须使用JNI,但如果它们是用Jython编写的,你可以轻松地从Java中调用它们,因为Jython最终会生成Java字节码。

现在当我说用cpython或jython编写时,这并没有太多意义,因为Python就是Python,除非你使用依赖于cpython或Java的特定库,否则大多数代码都可以在两种实现上运行。

请参见如何在Java中使用Python解释器。


5

根据您的需求,像XML-RPC这样的选项可能会很有用,它可以用于远程调用支持该协议的任何语言中的函数。


我15年前在一个项目中使用过那个,它非常复杂。至少使用JSON吧。 - WestCoastProjects

4
Jython有一些限制:
首先,Jython程序不能使用用C编写的CPython扩展模块。这些模块通常具有扩展名为.so、.pyd或.dll的文件。如果您想使用这样的模块,应该寻找等效的纯Python或Java编写的模块。虽然在技术上支持这些扩展是可行的 - IronPython就是这样做的 - 但是在Jython中没有计划这样做。
您可以使用Runtime或ProcessBuilder从Java中简单地调用Python脚本(或bash或Perl脚本),并将输出传回到Java:Running a bash shell script in java Running Command Line in Java
另外,您也可以使用Jython将Python脚本打包成JAR文件:Distributing my Python scripts as JAR files with Jython?

使用java runtime.getruntime()获取执行命令行程序的输出


3

7
任何可以从C中调用的语言都可以被称为C可调用语言。好吧,Python可以,但在这方面使用Jython确实是一个更简单的解决方案(或者在j6+中使用PyInterpreter)。编写代码以从C中调用Python函数并不是那么容易。 - Voo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接