在Java中调⽤Python
写在前⾯
在微服务架构⼤⾏其道的今天,对于将程序进⾏嵌套调⽤的做法其实并不可取,甚⾄显得有些愚蠢。当然,之所以要⾯对这个问题,或许是因为⼀些历史原因,或者仅仅是为了简单。恰好我在项⽬中就遇到了这个问题,需要在Java程序中调⽤Python程序。关于在Java中调⽤Python程序的实现,根据不同的⽤途可以使⽤多种不同的⽅法,在这⾥就将在Java中调⽤Python程序的⽅式做⼀个总结。
直接通过Runtime进⾏调⽤
我们知道,在Java中如果需要调⽤第三⽅程序,可以直接通过Runtime实现,这也是最直接最粗暴的做法。
public class InvokeByRuntime {
/**
* @param args
* @throws IOException
* @throws InterruptedException
*/
public static void main(String[] args) throws IOException, InterruptedException {
String exe = "python";
String command = "D:\\calculator_simple.py";
String num1 = "1";
String num2 = "2";
String[] cmdArr = new String[] {exe, command, num1, num2};
Process process = Runtime().exec(cmdArr);
InputStream is = InputStream();
DataInputStream dis = new DataInputStream(is);
String str = adLine();
process.waitFor();
System.out.println(str);
}
}
python转java代码输出:
3
calculator_simple.py:
# coding=utf-8
from sys import argv
num1 = argv[1]
num2 = argv[2]
sum = int(num1) + int(num2)
print sum
显然,在Java中通过Runtime调⽤Python程序与直接执⾏Python程序的效果是⼀样的,可以在Python中读取传递的参数,也可以在Java中读取到Python的执⾏结果。需要注意的是,不能在Python中通过return语句返回结果,只能将返回值写⼊到标准输出流中,然后在Java中通过标准输⼊流读取Python的输出值。
通过Jython调⽤
通过Jython调⽤Python?我在听到这个概念的时候⼀脸懵逼,不是说好的在Java中调⽤Python程序吗?这个Jython是什么⿁?难道是⼀个在Java中调⽤Python 程序的组件或⼯具?其实,关于Jython是什么这个疑问,我估计有许多⼈在⼀开始接触的时候也是很疑惑的,下⾯我们就⼀⼀道来。
1. 什么是Jython
Jython主页:
按照官⽅的定义,Jython是Python语⾔在Java平台的实现。这个概念似乎有点拗⼝,反正我⼀开始并没有理解。Python难道不已经是⼀门语⾔了吗?什么叫做Jython是Python语⾔在Java平台的实现?
实际上,之所以存在这样的困惑主要是因为我们对Python语⾔的相关概念掌握和理解不清楚导致的。
Python其实只是⼀个语⾔规范,它存在多个不同语⾔实现的版本。具体来说,⽬前Python语⾔存在如下⼏个具体实现:
(1)CPython:CPython是标准Python,也是其他Python编译器的参考实现。通常提到“Python”⼀词,都是指CPython。CPython由C编写,将Python源码编译成CPython字节码,由虚拟机解释执⾏。没有⽤到JIT等技术,垃圾回收⽅⾯采⽤的是引⽤计数。
(2)Jython:Jython是在JVM上实现的Python,由Java编写。Jython将Python源码编译成JVM字节码,由JVM执⾏对应的字节码。因此能很好的与JVM集成,⽐如利⽤JVM的垃圾回收和JIT,直接导⼊并调⽤JVM上其他语⾔编写的库和函数。
(3)IronPython:IronPython与Jython类似,所不同的是IronPython在CLR上实现的Python,即⾯向.NET平台,由C#编写。IronPython将源码编译成TODO CLR,同样能很好的与.NET平台集成。即与Jython相同,可以利⽤.NET框架的JIT、垃圾回收等功能,能导⼊并调⽤.NET上其他语⾔编写的库和函数。IronPython默认使⽤Unicode字符串。
(4)PyPy:这⾥说的PyPy是指使⽤RPython实现,利⽤Tracing JIT技术实现的Python,⽽不是RPython⼯具链。PyPy可以选择多种垃圾回收⽅式,如标记清除、标记压缩、分代等。
(5)Pyston:Pyston由Dropbox开发,使⽤C++11编写,采⽤Method-at-a-time-JIT和Mark Sweep——Stop the World的GC技术。Pyston使⽤类似JavaScript V8那样的多层编译,其中也⽤到了LLVM来优化代码。
所以,我们现在再来理解什么是Jython就⾮常清楚了:Jython是Python语⾔规范在Java平台的具体实现。具体来说,可以将Python源码编译为JVM可以解释执⾏的字节码。
Jython原本叫做JPython,于1997年由Jim Hugunin创建,后来在1999年2.0版本发布的时候由Barry Warsaw更名为Jython,在这⾥我们就不再深究为什么要把JPython更名为Jython的原因了。注意: Jython从2.0版本开始就与CPython的版本保持⼀致,即:Jython 2.7与CPython 2.7保持对应。
虽然我们理解了什么是Jython,但是还存在⼀个疑问,为什么Python语⾔存在这么多不同语⾔的实现呢?为什么不能就只存在⼀个C语⾔实现的版本就可以了呢?存在这么多版本,真的给初学者带来了许多困惑。
当然,要回答这个问题可能就需要深究⼀些历史的原因了,就此打住。我们在此只讨论使⽤Jython能做什么以及如何使⽤Jython?
2. 使⽤Jython能做什么
既然Jython是Python语⾔在Java平台的实现,是Java语⾔实现的,那么是否可以在Jython程序中调⽤Java,在Java中也能调⽤Jython呢?
答案是肯定的,实际上,Jython的主要通途就是在Java中调⽤Python程序;⽽且,还可以直接在Jython程序中引⽤Java。
3. 如何使⽤Jython
3.1 安装Jython
在Jython的官⽅下载页⾯我们可以看到如下描述(详见:
显然,可以下载2个Jython的jar包。其中,jython-installer-${version}.jar是⽤于安装Jython的,jython-
standalone-${version}.jar⽤于嵌⼊到Java程序中使⽤。什么意思?我⼀开始也是很疑惑,为什么要提供2个不同的jar包呢?他们有什么不同呢?2个不同的Jar包如何使⽤呢?
⾸先,jython-installer-${version}.jar⽤于安装Jython,就好⽐我们需要安装JRE,⽤于运⾏Java程序。除此之外,当需要在Python程序中引⽤⼀些公共的第三⽅库时,也需要先安装Jython才能下载所依赖的模块。
下载jython-installer-${version}.jar完毕之后,进⼊控制台,执⾏如下命令:
java -jar jython-installer-${version}.jar
此时会弹出⼀个图形化的安装界⾯,只需要⼀步⼀步选择相应参数进⾏安装即可。安装完毕之后,请将Jython安装⽬录添加为环境变量JYTHON_HOME,同时添加bin⽬录到PATH变量中:PATH=$PATH:$JYTHON_HOME/bin。
进⼊控制台,执⾏如下命令就可以进⼊Jython的交互环境,这与CPython(我们通常说的Python)的命令⾏交互环境是⼀样的。
> jython
Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11)
[Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.8.0_121
Type "help", "copyright", "credits" or "license" for more information.
>>> print("hello,world")
hello,world
>>>
当然,我们还可以使⽤jython命令运⾏⼀个Python程序。
> jython helloworld.py
hello,world
helloworld.py:
import sys
print("hello,world")
上⾯我们看到在Jython官⽹提供了2个Jar包,⼀个⽤于安装Jython,执⾏Python程序。那么,jython-standalone-${version}.jar⼜有什么⽤途呢?
实际上,当我们需要在Java中调⽤Python程序时,除了直接使⽤Java的Runtime调⽤,还可以直接使⽤Jython的API进⾏调⽤,⽽且通过Jython API可以直接调⽤Python程序中的指定函数或者对象⽅法,粒度更加精细。
当我们需要调⽤Jython的API时有两种⽅式:
第⼀,如果项⽬使⽤Maven进⾏构建,可以直接添加Jython的依赖配置到l⽂件中,如:
<dependency>
<groupId>org.python</groupId>
<artifactId>jython</artifactId>
<version>2.7.0</version>
</dependency>
第⼆,可以直接将jython-standalone-${version}.jar添加到项⽬classpath中,这样也可以调⽤Jython的相关API了。也就是说,jython-standalone-${version}.jar就是⼀个提供Jython API的jar独⽴jar包。
3.2 Java调⽤Python程序实践
Java通过Jython API调⽤Python程序,有⼏种⽤法:
(1)在Java中执⾏Python语句,相当于在Java中嵌⼊了Python程序,这种⽤法不常见,也没有太⼤的实际意义。
public static void main(String[] args) {
System.setProperty("python.home", "D:\\jython2.7.0");
PythonInterpreter interp = new PythonInterpreter();
// 执⾏Python程序语句
<("import sys");
interp.set("a", new PyInteger(42));
<("print a");
<("x = 2+2");
PyObject x = ("x");
System.out.println("x: " + x);
}
输出:
42
x: 4
(2)在Java中简单调⽤Python程序,不需要传递参数,也不需要获取返回值。
public static void main(String[] args) throws IOException {
System.setProperty("python.home", "D:\\jython2.7.0");
String python = "D:\\simple_python.py";
PythonInterpreter interp = new PythonInterpreter();
interp.cleanup();
interp.close();
}
simple_python.py:
# coding=utf-8
print("Do simple thing in Python")
print("输出中⽂")
(3)在Java中单向调⽤Python程序中的⽅法,需要传递参数,并接收返回值。Python既⽀持⾯向函
数式编程,也⽀持⾯向对象编程。因此,调⽤Python程序中的⽅法也分别以⾯向函数式编程和⾯向对象式编程进⾏说明。
public static void main(String[] args) throws IOException {
System.setProperty("python.home", "D:\\jython2.7.0");
// 1. Python⾯向函数式编程: 在Java中调⽤Python函数
String pythonFunc = "D:\\calculator_func.py";
PythonInterpreter pi1 = new PythonInterpreter();
// 加载python程序
// 调⽤Python程序中的函数
PyFunction pyf = ("power", PyFunction.class);
PyObject dddRes = pyf.__call__(Py.newInteger(2), Py.newInteger(3));
System.out.println(dddRes);
pi1.cleanup();
pi1.close();
// 2. ⾯向对象式编程: 在Java中调⽤Python对象实例的⽅法
String pythonClass = "D:\\calculator_clazz.py";
// python对象名
String pythonObjName = "cal";
// python类名
String pythonClazzName = "Calculator";
PythonInterpreter pi2 = new PythonInterpreter();
// 加载python程序
// 实例化python对象
<(pythonObjName + "=" + pythonClazzName + "()");
// 获取实例化的python对象
PyObject pyObj = (pythonObjName);
// 调⽤python对象⽅法,传递参数并接收返回值
PyObject result = pyObj.invoke("power", new PyObject[] {Py.newInteger(2), Py.newInteger(3)});
double power = Py.py2double(result);
System.out.println(power);
pi2.cleanup();
pi2.close();
}
输出:
8.0
8.0
calculator_func.py:
# coding=utf-8
import math
# ⾯向函数式编程
def power(x, y):
return math.pow(x, y)
calculator_clazz.py:
# coding=utf-8
import math
# ⾯向对象编程
class Calculator(object):
# 计算x的y次⽅
def power(self, x, y):
return math.pow(x,y)
(4)⾼级调⽤,也是在Java中调⽤Python程序最常见的⽤法:Python程序可以实现Java接⼝,在Python中也可以调⽤Java⽅法。public static void main(String[] args) throws IOException {
System.setProperty("python.home", "D:\\jython2.7.0");
// Python程序路径
String python = "D:\\python\\fruit_controller.py";
// Python实例对象名
String pyObjName = "pyController";
// Python类名
String pyClazzName = "FruitController";
Fruit apple = new Apple();
Fruit orange = new Orange();
PythonInterpreter interpreter = new PythonInterpreter();
// 如果在Python程序中引⽤了第三⽅库,需要将这些被引⽤的第三⽅库所在路径添加到系统环境变量中
// 否则,在执⾏Python程序时将会报错: ImportError: No module named xxx
PySystemState sys = SystemState();
sys.path.add("D:\\python");
// 加载Python程序
// 实例 Python对象
<(pyObjName + "=" + pyClazzName + "()");
// 1.在Java中获取Python对象,并将Python对象转换为Java对象
// 为什么能够转换? 因为Python类实现了Java接⼝,通过转换后的Java对象只能调⽤接⼝中定义的⽅法
GroovyController controller = (GroovyController) (pyObjName).__tojava__(GroovyController.class);
// 2.在Java直接通过Python对象调⽤其⽅法
// 既可以调⽤实现的Java接⼝⽅法,也可以调⽤Python类⾃定义的⽅法
PyObject pyObject = (pyObjName);
pyObject.invoke("controllFruit", Py.java2py(apple));
pyObject.invoke("controllFruit", Py.java2py(orange));
pyObject.invoke("printFruit", Py.java2py(apple));
pyObject.invoke("printFruit", Py.java2py(orange));
// 3.在Java中获取Python类进⾏实例化对象: 没有事先创建 Python对象
PyObject pyClass = ("FruitController");
PyObject pyObj = pyClass.__call__();
pyObj.invoke("controllFruit", Py.java2py(apple));
pyObj.invoke("controllFruit", Py.java2py(orange));
PyObject power = pyObj.invoke("power", new PyObject[] {Py.newInteger(2), Py.newInteger(3)});
if(power != null) {
double p = Py.py2double(power);
System.out.println(p);
}
interpreter.cleanup();
interpreter.close();
}
输出:
Show: I am a java apple.
controllFruit Python Apple
controllFruit END
Show: I am a java orange.
controllFruit Python Orange
controllFruit END
Show: I am a java apple.
controllFruit Python Apple
controllFruit END
Show: I am a java orange.
controllFruit Python Orange
controllFruit END
Show: I am a java apple.
printFruit Python Apple
printFruit END
Show: I am a java orange.
printFruit Python Orange
printFruit END
Show: I am a java apple.
controllFruit Python Apple
controllFruit END
Show: I am a java orange.
controllFruit Python Orange
controllFruit END
8.0
fruit_controller.py:
# coding=utf-8
from calculator_clazz import Calculator
from java.lang import String
st.inter import GroovyController
st.inter import Fruit
# 在Python中实现Java接⼝: st.inter.GroovyController class FruitController(GroovyController):
# 实现接⼝⽅法
def controllFruit(self, fruit):
# 在Python中调⽤Java对象⽅法
fruit.show()
Type() == "apple"):
print ("controllFruit Python Apple")
Type() == "orange"):
print ("controllFruit Python Orange")
print ("controllFruit END")
# ⾃定义新⽅法
def printFruit(self, fruit):
fruit.show()
Type() == "apple"):
print ("printFruit Python Apple")
Type() == "orange"):
print ("printFruit Python Orange")
print ("printFruit END")
# 引⽤第三⽅python程序
def power(self, x, y):
cal = Calculator()
return cal.power(x, y)
Java接⼝和实现类:
// 该接⼝⽤于在Python中实现
public interface GroovyController {
public void controllFruit(Fruit fruit);
}
// 在Java中使⽤的接⼝
public interface Fruit {
public String getName();
public String getType();
public void show();
}
// Apple
public class Apple implements Fruit {
public String getName() {
return "java apple";
}
public String getType() {
return "apple";
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论