用Java执行Python:Jython踩坑笔记

原创 2018年04月16日 17:39:25

常见的java调用python脚本方式

1.通过Jython.jar提供的类库实现
2.通过Runtime.getRuntime()开启进程来执行脚本文件


1.Jython

Jpython使用时,版本很重要!大多数坑来源于此。这句话不听的人还得走点弯路

运行环境:Python2.7 + Jython-standalone-2.7.0

<!--Maven依赖,jar包自行前往仓库下载-->
<dependency>
    <groupId>org.python</groupId>
    <artifactId>jython-standalone</artifactId>
    <version>2.7.0</version>
</dependency>

1)Jython执行Python语句

import org.python.util.PythonInterpreter;

public class HelloPython {
    public static void main(String[] args) {
        PythonInterpreter interpreter = new PythonInterpreter();
        interpreter.exec("print('hello')");
    }
}

2)Jython执行Python脚本

import org.python.util.PythonInterpreter;

public class HelloPython {
    public static void main(String[] args) {
        PythonInterpreter interpreter = new PythonInterpreter();
        interpreter.execfile("./pythonSrc/time.py");
    }
}

3)Jython执行Python方法获取返回值

PythonInterpreter interpreter = new PythonInterpreter();
interpreter = new PythonInterpreter(); 
interpreter.execfile("./pythonSrc/fibo.py"); 
PyFunction function = (PyFunction)interpreter.get("fib",PyFunction.class); 
PyObject o = function.__call__(new PyInteger(8));
System.out.println(o.toString());

fibo.py

 # Fibonacci numbers module  
def fib(n): # return Fibonacci series up to n  
    result = []  
    a, b = 0, 1  
    while b < n:  
        result.append(b)  
        a, b = b, a+b  ·
    return result 

2.Jython的局限

Jython在执行普通py脚本时速度很慢,而且在含有第三方库(requests, jieba…)时bug很多,不易处理。 原因在于,python执行时的sys.path和Jython的sys.path路径不一致,以及Jython的处理不是很好。

python执行时:

['F:\\Eclipse for Java EE\\workspace\\Jython\\pythonSrc', 'F:\\Python27\\DLLs', 'F:\\Python27\\lib', 'F:\\Python27\\lib\\lib-tk', 'F:\\Python27', 'F:\\Python27\\lib\\site-packages', 'F:\\Python27\\lib\\site-packages\\unknown-0.0.0-py2.7.egg', 'F:\\Python27\\lib\\site-packages\\requests-2.18.4-py2.7.egg', 'F:\\Python27\\lib\\site-packages\\certifi-2018.1.18-py2.7.egg', 'F:\\Python27\\lib\\site-packages\\urllib3-1.22-py2.7.egg', 'F:\\Python27\\lib\\site-packages\\idna-2.6-py2.7.egg', 'F:\\Python27\\lib\\site-packages\\chardet-3.0.4-py2.7.egg', 'C:\\windows\\system32\\python27.zip', 'F:\\Python27\\lib\\plat-win']

Jython 执行时:

['F:\\Maven\\repo\\org\\python\\jython-standalone\\2.7.0\\Lib', 'F:\\Maven\\repo\\org\\python\\jython-standalone\\2.7.0\\jython-standalone-2.7.0.jar\\Lib', '__classpath__', '__pyclasspath__/']

关于路径问题,我们有两种解决方法,一是手动添加第三方库路径,调用

        PySystemState sys = Py.getSystemState(); 
        System.out.println(sys.path.toString());
        sys.path.add("F:\\Python27\\Lib\\site-packages\\jieba"); 

二是把第三方库文件夹放到执行的.py脚本同级目录
然后新的问题来了,你以为路径是最终的问题吗?不止,也许是Python的版本语法问题,2x,3x,导致你在用Jython执行含第三方库的.py脚本时,各种Module不存在。原本博主走的Jython的路子,还下载了jieba第三方库,后来运行时一大堆错误:jieba库好像对py3有过渡支持,jython不支持这种语法格式,我改了jieba一处又一处,所以,博主在受过摧残之后果断放弃Jython!因为不能使用第三方库的python,然并卵!

最终方法来了:模拟控制台执行

public class Cmd {

    public static void main(String[] args) throws IOException, InterruptedException {
        String[] arguments = new String[] { "python", "./pythonSrc/time.py", "huzhiwei", "25" };
        try {
            Process process = Runtime.getRuntime().exec(arguments);
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = null;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
            in.close();
            int re = process.waitFor();
            System.out.println(re);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

time.py

#!/usr/bin/python
#coding=utf-8

#定义一个方法
def my_test(name, age):
    print("name: "+str(name))
    print(age)  #str()防解码出错
    return "success"
#主程序
#sys.argv[1]获取cmd输入的参数
my_test(sys.argv[1], sys.argv[2])

执行结果

name: huzhiwei
25
0

再唠两块钱的~
这个方法的局限性也来了,对python开发人员很简单,直接打印输出,但一个python模块只能做一件事,这点很像Python端给Java端的一个公开接口,类似Servlet吧?好处也有,不会出错,运行快!还在犹豫的同学赶快转cmd吧~!


2018-4-19:

博主在模拟cmd调用Python时遇到一些情况,这类问题可以归类为“超时,阻塞”等
问题原因:

Process p=Runtime.getRuntime().exec(String[] cmd);
Runtime.exec方法将产生一个本地的进程,并返回一个Process子类的实例,该实例可用于控制进程或取得进程的相关信息。
由于调用Runtime.exec方法所创建的子进程没有自己的终端或控制台,因此该子进程的标准IO(如stdin,stdou,stderr)都通过
    p.getOutputStream(),
    p.getInputStream(),
    p.getErrorStream()
方法重定向给它的父进程了.用户需要用这些stream来向 子进程输入数据或获取子进程的输出。
    例如:Runtime.getRuntime().exec("ls")
另外需要关心的是Runtime.getRuntime().exec()中产生停滞(阻塞,blocking)的问题?
这个是因为Runtime.getRuntime().exec()要自己去处理stdout和stderr的输出,
就是说,执行的结果不知道是现有错误输出(stderr),还是现有标准输出(stdout)。
你无法判断到底那个先输出,所以可能无法读取输出,而一直阻塞。
例如:你先处理标准输出(stdout),但是处理的结果是先有错误输出(stderr),
一直在等错误输出(stderr)被取走了,才到标准输出(stdout),这样就产生了阻塞。

解决办法:

 用两个线程将标准输出(stdout)和错误输出(stderr)。

完整代码:

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * Created by yster@foxmail.com 2018年4月19日 下午1:50:06
 */
public class ExcuteCmd {
    /** 执行外部程序,并获取标准输出 */
    public static String excuteCmd_multiThread(String[] cmd, String encoding) {
        BufferedReader bReader = null;
        InputStreamReader sReader = null;
        try {
            Process p = Runtime.getRuntime().exec(cmd);

            /* 为"错误输出流"单独开一个线程读取之,否则会造成标准输出流的阻塞 */
            Thread t = new Thread(new InputStreamRunnable(p.getErrorStream(), "ErrorStream"));
            t.start();

            /* "标准输出流"就在当前方法中读取 */
            BufferedInputStream bis = new BufferedInputStream(p.getInputStream());

            if (encoding != null && encoding.length() != 0) {
                sReader = new InputStreamReader(bis, encoding);// 设置编码方式
            } else {
                sReader = new InputStreamReader(bis, "utf-8");
            }
            bReader = new BufferedReader(sReader);

            StringBuilder sb = new StringBuilder();
            String line;

            while ((line = bReader.readLine()) != null) {
                sb.append(line);
                sb.append("\n");
            }

            bReader.close();
            p.destroy();
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "result: error";
        } finally {
        }
    }
}

class InputStreamRunnable implements Runnable {
    BufferedReader bReader = null;
    String type = null;

    public InputStreamRunnable(InputStream is, String _type) {
        try {
            bReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is), "UTF-8"));
            type = _type;
        } catch (Exception ex) {
        }
    }

    @SuppressWarnings("unused")
    public void run() {
        String line;
        int lineNum = 0;

        try {
            while ((line = bReader.readLine()) != null) {
                lineNum++;
                // Thread.sleep(200);
            }
            bReader.close();
        } catch (Exception ex) {
        }
    }
}

使用时直接调用该工具类即可。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yueshutong123/article/details/79963007

Python分词模块推荐:结巴中文分词

就是前面说的中文分词,这里需要介绍的是一个分词效果较好,使用起来像但方便的Python模块:结巴。 一、结巴中文分词采用的算法 基于Trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词...
  • zhaixh_89
  • zhaixh_89
  • 2013-12-24 17:00:12
  • 2081

JAVA调用jython执行python文件

在web开发时候,经常在web环境使用本地环境的第三方库什么的,本文讲解java如何执行python文件。 网上说方法有三种,其实也就两种,下面着中介绍第二种通过(jython)。方法一java...
  • tengxing007
  • tengxing007
  • 2017-03-21 22:18:27
  • 2394

Jython-Java操作Python脚本学习笔记(二)

这里将记录关于使用pydev插件在Eclipse中开发Python,同时使用Jython来实现Java与Python的互操作:    (一)关于下载安装和配置pydev插件和Jython:    ...
  • wodestudy
  • wodestudy
  • 2012-11-16 15:49:01
  • 16321

使用jython和ScriptEngineManager在java中执行python

方法一: 使用java内置的ScriptEngineManager进行python脚本调用,只要是在java6及以上即可。 1、 直接运行python语句: /** * 使用ScriptEng...
  • Anlegor
  • Anlegor
  • 2015-03-05 19:32:50
  • 3104

Learning Python -- Java 通过JyThon调用Python实现的规则

学习Python的时候,发现Java能够通过JyThon调用 Python
  • supermig
  • supermig
  • 2014-04-18 13:28:06
  • 2874

Jython:java调用python文件之第三方包路径问题

本方法解决python代码的可移植性,不需要在新机器上配置python环境,只通过安装jython的方式将python代码嵌入java工程 1. Jython如何安装 下载地址:jython_inst...
  • ztf312
  • ztf312
  • 2016-05-07 15:40:57
  • 14306

Java调用Python开发环境配置(Eclipse+Jython+PyDev)

eclipse中通过jython让java调用python的方法。
  • xfei365
  • xfei365
  • 2016-03-09 14:36:49
  • 2708

Bootstrap踩坑经历

Bootstrap是一款十分受欢迎的前端响应式框架,里面几乎包含了所有你能想到的优秀组件,设计出来的网站十分炫酷,但是使用过程并没有想象中的那么顺利,这里发布一下使用Bootstrap时所可能遇到的'...
  • z403877967
  • z403877967
  • 2017-06-07 16:04:13
  • 564

Java 开发中如何正确踩坑

动力节点 2017-05-31 09:06 官方微信:动力节点Java学院 为什么说一个好的员工能顶 100 个普通员工 我们的做法是,要用最好的人。我一直都认为研发本身是很有创造性的,如果人不放...
  • u011277123
  • u011277123
  • 2017-06-01 21:39:19
  • 366

Jython和Java相互调用

1. 用Jython调用Java类库  第一步、创建Java类  写一个简单的Java类,用Point来示例:  Java代码   import org.p...
  • lzujiangge
  • lzujiangge
  • 2012-11-13 19:58:40
  • 7494
收藏助手
不良信息举报
您举报文章:用Java执行Python:Jython踩坑笔记
举报原因:
原因补充:

(最多只允许输入30个字)