Different / better approaches for calling python f

2019-01-28 09:41发布

问题:

I am quite new to python and am trying to call python's function from java.

My primary requirements are these:

  • call should be transparent, in the sense that it should not require modifying .py file simply to enable it to be called from java. I might be given any python file with some functions inside it. I should be able to call any of these functions without requiring to modify .py file.
  • I want to be able to send arguments of both primitive types (int, String, floats etc.) or non primitive types (HashMap,ArrayList) from java to python function and receive back the returned object (which may of primitive types or non-primitive types) from python to java. I am also using pandas DataFrame and numpy ndarray and hence also want to be able to send and receive corresponding objects to and from java.
  • I preferably want to stick to CPython instead of Jython because I might need to use newer libraries that might not be available in Jython.

There are several options that I found online. Few are:

  • Use Jython's PythonInterpreter with which I am able to invoke python function without requiring any changes to .py script file:

    py1.py

     def square2(list):
         squares = []
         for i in list:
             squares.append(i*i)
         return squares
    

    JythonTest.groovy

     import org.python.util.PythonInterpreter
     import org.python.core.*;
    
     class JythonTest
     {
          static main(def args)
          {
              PythonInterpreter pi = new PythonInterpreter()
              pi.exec("from py1 import square2")
              PyFunction pf = (PyFunction)pi.get("square2")
              println pf.__call__(new PyList([1,2,3,4]))[2]   //9
          }
     }
    

    I am very well able to satisfy my needs. But its not CPython.

  • Use ScriptEngine: This is very similar to PythonInterpreter. But agains its Jython. Also, unlike PythonInterpreter, we cannot work with Jython 2.5+ and cannot directly access PyObjects. So this option can be very well closed.

  • Use py4j: Cant find example which is as minimal as in case of Jython PythonInterpreter
  • Use java2python. But not much information is given about calling python from java so that I can conclude whether my above requirements can be satisfied. Can anyone shed more light on this? More specifically if we can write the code as minimal as the one in Jython PythonInterpreter.
  • Use JPype: However after quick go through I feel I will not be able to write as minimal code as in case of Jython PythonInterpreter. Also I felt the project somewhat not under developement. Is it?

It seems that Jython PythonInterpreter is the best choice if I have understood all above approaches correctly. Have I made mistakes while grasping them? Also is there any other better option?

回答1:

There is no current answer to this problem. Using CPython relies on the execution of Python bytecodes, which in turn requires that the Python interpreter be embedded in the execution environment. Since no Java runtime comes with an embedded Python interpreter, it really does look as though Jython is the best answer.

Sometimes the answer you want just isn't available!



回答2:

@Mahesha999, regarding the ability to stick with CPython which seems important now from your last comment:

Jep is a good option to be able to run python code which uses calls to native, like pandas that you mentionned.

You'll need to write some wrapping code because Jep only implements automatic conversion between Java and Python between the most used types, which pandas.DataFrame is not.

However if your use case is not to complex, you can access your pandas objects as numpy.NDArray object by calling DataFrame.values on your dataframe instance, and Jep implement conversion to the Java class it embeds for NDArray.

You can get back to Java values from python code you execute by using Jep.getValue(String pythonVariableName, Class clazz)

For example

Jep jep = new Jep();
jep.eval("import my_script");
jep.eval("df = my_script.function_returning_a_dataframe()");
jep.eval("col = df.a_column.values");
NDArray myCol = jep.getValue("col", NDArray.class);

I do so on a project I coded in Python that I need to integrate as a plugin in a Java application, so far it works.