Python程序员都在看的公众号,跟着编程派一起学习Python,看最新国外教程和资源! |
|
Python中文社区 · 超爆回报2682%!轻松复制这个简单暴利的量 ... · 昨天 |
|
Python爱好者社区 · 模仿一下领导说话的样子 · 15 小时前 |
|
Python爱好者社区 · 今年程序员这薪资是认真的吗? · 昨天 |
|
Python爱好者社区 · Python接入DeepSeek,太强了! · 2 天前 |
|
Python爱好者社区 · 35个爬虫实例 · 3 天前 |
投稿作者:刘利强
原文:http://liuliqiang.info/post/166/
可能很多人都听说过 Jython ,或者也叫 JPython ,如果没听过的话,我大概介绍一下, Jyhton 就是在 Java 虚拟机(JVM) 上运行 python 代码的一种语言,代码是用 python 编写的,然后编译成 JVM 可以解析的字节码,运行在 JVM 上。
很明显, Jython 是给 Java程序员 运行 Python 程序用的,如果是 Python程序员 想运行 Java代码 怎么办?方式还是很多的,有 JPype/JEP/JPE 等等,这些项目都可以在 Python 运行环境执行 Java代码 ,那么,本文就以 JPype 为主题,介绍一下如何在 Python代码中运行 Java 代码 ,包括但不限于直接编写 Java代码 , 加载jar包 和 执行jar包 。
在开始之前,先介绍一下本文使用的 Python 和 Java 运行环境
Java 版本
在本文中,我运行的 Java 版本是 1.8,是 Oracle 的 Hotspot 版,不是 OpenJDK,但是对于本文的内容,OpenJDK 也适用,亲测。
>
java
-
version
java version
"1.8.0_45"
Java
(
TM
)
SE
Runtime
Environment
(
build
1.8
.
0_45
-
b14
)
Java
HotSpot
(
TM
)
64
-
Bit
Server
VM
(
build
25.45
-
b02
,
mixed mode
)
Python 版本
在本文中,我使用的 Python 版本为 3.5.2,这里有个很尴尬的问题,Python 不得不吐槽的一点就是版本太乱,2.6, 2.7, 3.4/3.5 这三个版本应该是比较多人用的,但是 不兼容!! ,而且大部分系统默认依赖的是 2.7,所以当你在项目使用时,一定要注意版本的选择,我目前推荐 3.5。
但是,不用担心,对于本文来说, JPype 是支持 2.7 和 3.5 的,所以,你使用本文的代码是完全可以在 2.7 上跑通的,而且更舒适的是在 2.7 上安装 JPype 比 3.5 上方便多了。
在 Python 3.5 中,要安装 JPype 你不能直接使用 pip3 安装,因为 JPype 默认是为 Python2 编写的(其实是开发那时Python3还未进入主流,并且早在 2009 年就断更了),但是,不要紧张,有专业人士为 Python3 编写了另外一个兼容的版本,所以我们需要从源码安装了,具体步骤为:
> git clone https://github.com/tcalmant/jpype-py3.git
> cd jpype-py3
> python3 setup.py install
然后就会编译安装了,正常的话是可以顺利安装成功地,这样安装也就完事了。
如果编译出错的话,请检查一下你的系统是否缺乏
python3
-
dev
库或者根本没有安装
C++
编译工具,如果在 Linux 的话,你可以直接敲这样的命令:
>
sudo apt
-
get install g
++
python3
-
dev
如果还失败的话,那么可能你需要制定一下 JAVA_HOME ,在尝试安装:
这里的 JAVA_HOME 需要根据你的 JDK 情况进行修改,千万不要照抄!!!
>
JAVA_HOME
=
/usr/
lib
/
jvm
/
java
-
1.7
.
0
-
openjdk
-
amd64 python3 setup
.
py install
如果还有问题,那我就没办法了,来项目的 GITHUB 提 ISSUE 吧。
安装完了之后,我们就要开始写代码了,下面我将会介绍一些常用的套路,依照这些套路,你应该可以顺利得完成大部分的工作需求了。
一开始肯定要来个最简单的 ”Hello World“ 了,毕竟都是国际惯例了,所以啥也不多说了,先来一小段代码让大家看看,如果简单快速得使用 JPype :
import jpype
from jpype import *
if __name__ == "__main__":
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("Hello World")
shutdownJVM()
可以看到,这里的代码很简单,寥寥三句而已,开头的
import
是必须的套路了,然后关键代码都在
__main__
里面,可以看到的是
__main__
里头只有三句代码,其中第一句和第三句都是和
JVM
密切相关的,那就是
startJVM
和
shutdownJVM
,其实就是开启和关闭
JVM
的执行环境;然后中间这一句才是真正执行的
Java代码
,可以看到,除了和在
Java
中直接调用
System
.
out
.
println
多了前面的包名之外,其他都是一样的,所以这让
Python
使用
Java代码
变得非常简单。
入门第一坑
可能有时我们在代码中只会偶尔跑一下
Java代码
,并不会一直运行着,这时候,为了性能考虑,可能回想在运行完
Java代码
之后会关掉
JVM
,然后在下次运行
Java代码
的时候再启动一次
JVM
。说实话,这个想法是挺好的,所以我们来实践一遍:
import jpype
from jpype import *
if __name__ == "__main__":
print(jpype.getDefaultJVMPath())
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("First time, i am running")
shutdownJVM()
# Here we do a lot of work
startJVM(jpype.getDefaultJVMPath())
java .lang.System.out.println("Sencond time, i am coming back")
shutdownJVM()
然后我们执行一下,well,悲剧发生了,我们来看看输出:
First time, i am running
JVM activity report :
classes loaded : 31
JVM has been shutdown
Traceback (most recent call last):
File "/Users/yetship/Code/python/JpypeExample/RestartJvm.py", line 12 , in <module>
startJVM(jpype.getDefaultJVMPath())
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/JPype1_py3-0.5.5.2-py3.5-macosx-10.6-intel.egg/jpype/_core.py", line 58, in startJVM
_jpype.startup(jvm, tuple(args), True)
RuntimeError: Unable to start JVM at native/common/jp_env.cpp:60
没错,在第二次启动
JVM
的时候出错了,
JVM
居然启动不起来了!!!是的,不要惊讶,这是 BUG,而且是很久的 BUG,作者也不准备改的BUG,ISSUE 84 说了这个问题。
判断 JVM 是否启动
刚才第一坑说了,不能重启,那我们只能保持
JVM
一直运行了,同时,为了放置误操作重启了
JVM
,所以
JPype
提供了另外一个函数判断
JVM
是否已启动,这里也给出一个示例看看:
import jpype
from jpype import *
if __name__ == "__main__":
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("First time, i am running")
# Here we do a lot of work
if not isJVMStarted():
startJVM(jpype.getDefaultJVMPath())
java.lang.System.out.println("Sencond time, i am coming back")
shutdownJVM ()
这里的第 9 行判断了一下
JVM
是否已经启动,然后再决定要不要启动
JVM
,最后再运行
Java代码
。
引用 jar 包
在使用
Java
的时候,你说不引用
第三方 jar 包
是不可能的,因为
Java
之所以流行,和他拥有大量的第三方库是分不开的,所以,这里必须要介绍一下这个功能,如何引用第三方 jar 包,这里就以
alibaba
出品的
fastjson
为例,介绍一下如何使用第三方 jar 包
下载 jar 包
下载链接: https://search.maven.org/remote_content?g=com.alibaba&a=fastjson&v=LATEST
引入 jar 包
在 JPype 中引入 jar 包和我们平时执行 Java 命令时相似,回想一下我们平时指定 jar 包时,使用的是 -cp 参数,然后后面接 jar包的位置,这里也一样
jvm_args
=
"-Djava.class.path=ext_classpath"
代码示例
import
json
from
jpype
import
*
if
__name__
==
"__main__"
:
jars
=
[
"/Users/yetship/tools/libs/fastjson-1.2.21.jar"
]
jvm_path
=
getDefaultJVMPath
()
jvm_cp
=
"-Djava.class.path={}"
.
format
(
":"
.
join
(
jars
))
startJVM(jvm_path, jvm_cp)
JSONObject = JClass("com.alibaba.fastjson.JSONObject")
json_str = json.dumps({"name": "yetship", "site": "https://liuliqiang.info"})
jsonObj = JSONObject.parse(json_str)
print(jsonObj.getString("name"))
print
(jsonObj.getString("site"))
shutdownJVM()
可以看到,这里获取了
JSONObject
的对象,然后解析了一个 json 字符串,然后从解析出来的 JSON 对象中获取值,这就是一个使用第三方 jar 包的应用,从这开始,你就开启了 Python 调用 Java 代码广泛的天空。
执行 jar 包
可能引用第三方 jar 包我们还不满意,我们还希望能够直接执行 jar 包,其实执行 jar 包未必需要使用 JPype来做,我们直接使用
subprocess
来做也行,但是这样管理就变得稍微有点复杂了,为了方便管理,统一代码,所以这里介绍一下如何使用 JPype 执行 jar 包。
首先,我们必须知道的是,所以得运行 jar 包,其实就是我们在打 jar 包的时候都会在
META
-
INF
/
MANIFEST
.
MF
指定了默认调用哪个类的 main 方法而已,这里不是讲 Java 知识的文章,所以就到此了,我们需要知道的是所谓的执行 jar 包其实就是调用了一个 main 方法而已,所以这里可以这么来调用一个 jar 包:
制作 jar 包:
java 代码为:
package
info
.
liuliqiang
;
public
class
Main
{
public static void main(String[] args)
{
System.out.println("I am called");
}
}
执行 jar 包
from
jpype
import
*
if
__name__
==
"__main__"
:
jars
=
[
"/Users/yetship/tools/libs/SimpleJar.jar"
]
jvm_path
=
getDefaultJVMPath
()
jvm_cp
=
"-Djava.class.path={}"
.
format
(
":"
.
join
(
jars
))
startJVM
(jvm_path, jvm_cp)
# 获取 Main 类
Main = JClass("info.liuliqiang.Main")
# 执行 main 函数,注意参数是 String[] args
Main
.main([])
shutdownJVM()
更加深入
类型对应
不同的语言支持的数据类型是不一样的,例如 Java 中有 int,long,double,float 等类型,而 python 中却是没有 double 的,只有 float,所以如何转换这些类型是个问题,这里就列举出转换的关系:
第一行表示 Java 中的数据类型
第一列表示 Python 中的数据类型
表格中没有数据表示不能转换
E 表示需要明确自己转换
I/I(x) 表示可以默认转换
X/X(x) 表示可以精确匹配
内部类
虽然在 JPype 中,Java 的特性受到了一些限制,但是内部类却还是可以使用的,但是,在使用的过程中,有一些内容你还是需要注意的:
内部类在 Java 中是用
$
符号与所在的主类隔开的,所以在查找类的时候需要带上
$
符号,例如
Boo$Foo
因为是内部类,所以你不能像其他标准的的类一样直接通过全路径包名访问内部类,你需要使用
JPackage
的
__getclass__
函数来加载
对于非静态的内部类在 Python 中将无法实例化,其他内部类实例将和普通类一样使用。
性能
对于跨语言调用,我们离不开的一个话题就是性能了,对于 JPype 来说,因为它低层使用的是 JNI;JNI 在 Java 中被用来和低层的 C/C++ 代码进行交互,因此可以发现 JPype 其实不是简单得做了一层转换,实际上是做了两层的转换。
但事实上,在 Python 中使用 Java 代码有点类似于在 Python 中部分用 C 代码编写一般,不仅不会降低 python 代码的性能,反而会提高 python 代码的性能。
既然是跨语言,那么当频繁交互的时候必然带来开销,如果你经常往 Java 代码中传递相同的对象(例如 String/Object/Array)的话,那么你可以使用包装器来预转换,这样多少能提升一些代码的执行速度。
多线程
好了,到了 Python 的软项了,熟悉 Python 的同学都知道 Python 没有真正的多线程,所以只有伪多线程,这就导致了调用 Java 代码也没有多线程,同时
Java代码中的多线程是不会生效的
。
JProxy
JProxy
是一个能让你在 Python 代码中实现任意数量的
Java
接口的方式。使用
JProxy
可以很简单得实现接口,
JProxy
支持两种方式实现接口:
方式一:指定实例
指定实例就是指
JProxy
接收两个参数:
举个例子,Java 代码中的接口是这样的:
然后再 Python 中可以这么实现:
第一个参数是要实现的接口,或者接口列表
第二个参数就是一个 Python 类,这个类已经实现了这些接口的所有方法
class
C
:
def
testMethod
(
self
)
:
|
Python中文社区 · 超爆回报2682%!轻松复制这个简单暴利的量化策略(附详细Python代码) 昨天 |
|
Python爱好者社区 · 模仿一下领导说话的样子 15 小时前 |
|
Python爱好者社区 · 今年程序员这薪资是认真的吗? 昨天 |
|
Python爱好者社区 · Python接入DeepSeek,太强了! 2 天前 |
|
Python爱好者社区 · 35个爬虫实例 3 天前 |
|
科学家庭育儿 · 婴幼儿“肚子疼”常见的七种可能性都不可小觑 8 年前 |
|
加拿大约克论坛 · 为您送上$300,助您进一步做好退休准备! 8 年前 |
|
闹闹每日星运 · 星历0225:宜写是急性子or慢性子 逛街 巨蟹陷入沉思 7 年前 |
|
资本圈的那些事 · 听说,大佬们的金句库又更新了...... 7 年前 |
|
上海发布 · 【注意】受雷雨天气影响,浦东、虹桥机场部分航班取消 7 年前 |