Calling java from python

PyJa — calling Java from Python part 1

As mentioned in the previous article here is the second part of the story: calling Java from Python.

Calling Java from Python

As I mentioned there exists a way in the other direction too: you can access Java classes and methods from your Python code. Now let me describe some methods. In this section I won’t call any Dropbox API either. Besides this, as I always mention, the Python API has a bit more functionality than the Java one.

Py4J

As the first candidate I’ll take a look at Py4J.

As the documentation mentions, Py4J can access Java objects in a JVM. So you have to run your Java application parallel to the Python script with Py4J. This means that you cannot write an application I did in the previous article as a sample: add up some numbers provided as program arguments.

But the tutorial at the homepage is very good. It shows you how to create Java classes (applications to be correct) which are accessible from the Python code — with Py4J. And the example application will be the same as previously — adapted to the requirements of Py4j.

Installation

To get Py4J running you need to install it as a Python module and get the JAR to your Java application’s class path to use the required classes. Py4J installs to Python very easily (if you have pip installed):

And for the Java part: create a maven project and add the dependency or with a simple Java application download the JAR and add it to your class path. As for me I’ll take the simple Java application approach and get the 0.8.1 version of the application.

Example

The example application is simple: first I show you the Java code, then the Python which calls the Java.

public class Py4JExample < public static void main(String[] args) < GatewayServer gatewayServer = new GatewayServer(new Py4JExample()); gatewayServer.start(); >public int addNumbers(int. numbers) < int sum = 0; for(int i : numbers) < sum += i; >return sum; > >

The method “addNumbers” speaks for itself: it adds up the provided int parameters and returns the value of the sum. The highlighted lines represent the Py4J binding: the first line creates a GatewayServer for the class to be available for the Python script and the second line starts it.

from py4j.java_gateway import JavaGateway import sys gateway = JavaGateway() app = gateway.entry_point nums = gateway.new_array(gateway.jvm.int, len(sys.argv)) for i in range(1, len(sys.argv)): nums[i-1] = int(sys.argv[i]) print("The result is: %d"%app.addNumbers(nums))

The script creates a JavaGateway object and get its entry point (the class we added to the GatewayServer’s constructor in the Java example).

The arguments provided to the Python script have to be converted into a Java array because varargs cannot be used as a method parameter when calling Java from Python. In Py4J you have some converters to use Python lists, maps and sets in Java. As one add-on to my previous article, Py4J has a feature to implement Java interfaces and allot them to be called from Java (as a callback).

Читайте также:  Таблицы

What if you want to use Py4J to call the Java Dropbox API? You have to implement a class which creates a GatewayServer with the one class which has the methods you want to call. If you want to call multiple classes, you have to create more classes which run separate GatewayServers and pass the required parameters between them or you write a class which exposes all relevant methods from the different objects and add this class as an access point.

So this solution isn’t easy too. Besides this you can reach with the started endpoint the core Java features available in every JVM: so java.utils, java.io, java.math and so on, the core libraries which do not require any dependency as we think about it as a maven project.

The release cycles at Py4J are not constant, between 0.7 and 0.8 there have been almost two years, between 0.8 and 0.8.1 almost 1 year. The developer is keen on making the library better as he encourages issues and replies to them.

JPype

JPype is an old tool, the last change has been in 2011. So I would say this project is dead — on the original SourceForge repository and the official web-site. However there is a GitHub project which continues the development of JPype. The last changes in the sources were recently. This means that JPype has been resurrected.

Installation

For windows there are some exe installers available. For my Mac I downloaded the zip’d installer and it did not work. The command

yielded an exception: the include file “jni.h” was not found. So I looked up where is this file located on my Mac and I found that I have to add

/System/Library/Frameworks/JavaVM.framework/Headers/

to the include folders of the setup script. It needs a bit tinkering. And if we saw the requirement of jni.h which I have to explain I guess: JPype is C++ based, and it gives a native Java experience: it connects to the JVM natively. So in this case you do not have to launch a server, which exposes all the needed methods of one single class.

Example

The examples at the GitHub page are older too (last change 2 years ago) but I think they should work.

In this article I’ll stick to my example: call a Java class which sums up an array of numbers provided as the arguments of the Python script. If you want to use a Java class, you have to compile it first. After this you can call it — you have to add it to the JVM starting method as a class path entry.

You can add JAR files to the class path too at starting the JVM so if you have a library which you want to use you can do it. The main part is to configure the class path accordingly.

In this example I’m going to call this Java class (both of the methods):

public class JPypeExample < public static int addition(Integer. numbers) < int sum = 0; for(int i : numbers) < sum += i; >return sum; > public int addNumbers(int a, int b, int c, int d) < return a+b+c+d; >>
import jpype import os jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=%s"%os.path.abspath(".")) JPypeExample= jpype.JClass("JPypeExample") numbers = jpype.JArray(jpype.java.lang.Integer, 1)(4) numbers[0] = jpype.java.lang.Integer(13) numbers[1] = jpype.java.lang.Integer(1) numbers[2] = jpype.java.lang.Integer(20) numbers[3] = jpype.java.lang.Integer(8) jPypeExample = JPypeExample() print "The result is: %d"%jPypeExample.addNumbers(13,1,20,8) print "The result is: %d"%JPypeExample.addition(numbers) jpype.shutdownJVM()

This shows that you should use the full path to your compiled Java class (in my example these two are in the same folder).

Читайте также:  Python selenium get started

The array is needed to fulfill the var-args contract in the Java class. I tried to figure out how to use a primitive int array but it will not work out.

OK. I tried. I got JPype compiled on my System. I created a super Java class to demonstrate the functionality. I wired Python up. But helpless. I get an exception of:

Traceback (most recent call last): File "jpype_example.py", line 6, in example = JClass("JPypeExample") File "/Library/Python/2.7/site-packages/jpype/_jclass.py", line 54, in JClass raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name) jpype._jexception.ExceptionPyRaisable: java.lang.Exception: Class JPypeExample not found

I googled after a solution. I found one interesting StackOverflow issue which has been solved but it did not help. There was the different Python and Java Compiler bit-version the cause. On my Mac there is no such problem: I’ve the 64 bit of both.

After a bit more searching I found that JPype is hard coded to the Java 6 version. This is bad because I have a Java 7 compiler as default. Nevertheless I compiled the Java class with

/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/javac JPypeExample.java

and this works as a miracle. My python script is executing and everything is fine. I’ve added the compiled class to the repository too to avoid problems.

Next time

This is the end of this article. It covers two libraries for Java access from Python. The next time I’ll take Jython and Pyjnius and make my examples with them.

Источник

Py4J – мост между Python и Java

Название Py4J можно встретить разве что в списке библиотек, используемых PySpark, но не стоит недооценивать данный инструмент, который обеспечивает совместную работу Python и Java. В этой статье будет кратко описана работа Py4J, рассмотрен пример использования и перечислены сильные и слабые стороны библиотеки. В конце будут описаны альтернативные способы связи Java и Python.

Py4J позволяет программам, работающим в интерпретаторе Python, динамически обращаться к объектам Java внутри JVM. Методы вызываются так, как если бы объекты Java находились в интерпретаторе Python, а доступ к коллекциям Java можно было получить с помощью стандартных методов коллекций Python. Py4J также позволяет программам Java вызывать объекты Python, но на этом возможности библиотеки не заканчиваются. Py4J позволяет создавать из Python классические коллекции Java, а именно Array, List, Set и Map. Также можно конвертировать Python коллекции в Java коллекции. Ещё из Python возможно реализовывать интерфейсы, которые описаны в Java.

Существует несколько альтернатив, например, Jython. В отличие от него, Py4J не выполняет код Python в JVM, поэтому при использовании Py4J можно работать со всеми библиотеками классического Cython. Ещё есть JPype, но Py4J не связывает потоки Python и Java, также он использует сокеты, а не JNI для связи с JVM.

Читайте также:  Таблицы

Главным минусом Py4J является скорость передачи данных между Python и Java. Так как общение происходит посредством сокетов, передавать данные, размером больше чем несколько мегабайт, будет плохой идеей, для этой цели существуют другие решения. Главная задача Py4J – обеспечить доступ к объектам Java и сохранить возможность использования Python библиотек.

Пример работы

Для примера будет создано три Java класса и один Python скрипт для проверки работы. Класс Dict, в котором реализована логика работы с HashMap, Класс DictEntryPoint, в котором реализована точка входа, чтобы Python мог взаимодействовать с объектом класса Dict. Также нужен класс Program, в котором происходит запуск сервера.

Для успешной работы Py4J нужно настроить точку входа, в которой создать необходимые для работы объекты, и запустить GatewayServer, который обеспечивает связь Python с JVM через сокет локальной сети. Далее можно спокойно использовать Java код из Python.

 package py4j_example; import java.util.HashMap; public class Dict < private HashMapdict = new HashMap<>(); /** * Добавление элемента по ключу */ public void add(Integer key, String value) < dict.put(key, value); >/** * Добавляет значение в конец HashMap */ public void add(String value) < dict.put(dict.size(), value); >/** * Удаляет значение по ключу */ public String remove(Integer key) < return dict.remove(key); >/** * Возвращает значение по ключу */ public String get(Integer key) < return dict.get(key); >/** * Возвращает длину HashMap */ public int length() < return dict.size(); >/** * Пересоздаёт HashMap */ public void clear() < dict = new HashMap<>(); add("Запись из Java"); > > 
 package py4j_example; import py4j.GatewayServer; public class DictEntryPoint < private final Dict dict; /** * Создаёт объект класса Dict */ public DictEntryPoint() < this.dict = new Dict(); >/** * Возвращает объект класса Dict */ public Dict getDict() < return this.dict; >> 
 package py4j_example; import py4j.GatewayServer; public class Program < public static void main(String[] args) < /* Создание и запуск сервера */ GatewayServer gatewayServer = new GatewayServer(new DictEntryPoint()); gatewayServer.start(); System.out.println("Gateway Server Started"); >> 

Вывод должен быть примерно такой. После запуска Program.java можно приступить к написанию Python кода для подключения к JVM.

 Connected to the target VM, address: '127.0.0.1:58425', transport: 'socket' Gateway Server Started 
 from py4j.java_gateway import JavaGateway def add_some_values(dict): # Создание списка имён и их запись в dict names = ["Вася", "Аня", "Лена", "Никита"] for name in names: dict.add(name) # Удаление значения по ключу 4 deleted = dict.remove(4) # Вывод содержимого dict for i in range(dict.length()): print(i, "\t", dict.get(i)) print("\nУдалённый элемент =", deleted) return 1 def main(): # Инициализация JavaGateway gateway = JavaGateway() # Получение доступа к объекту класса Dict dict = gateway.entry_point.getDict() add_some_values(dict) # Очистка Dict dict.clear() return 0 if __name__ == "__main__": main() 

После запуска Application.py в консоль выводится следующее:

 0 Запись из Java 1 Вася 2 Аня 3 Лена Удалённый элемент = Никита 

По ключу 0 находится элемент, добавленный из Java, а имена людей были добавлены с помощью Python.

Заключение

Py4J позволяет быстро и легко взаимодействовать с объектами Java из Python, организовывать двустороннее управление коллекциями и работать с интерфейсами в двух языках, соединяя интерпретатор и JVM сокетами. Он отлично подойдёт для задач, требующих минимальных ограничений на использование Python, но при этом не отличается высокой производительностью.

На правах рекламы

VDSina предлагает быстрые серверы в аренду под любые задачи, огромный выбор операционных систем для автоматической установки, есть возможность установить любую ОС с собственного ISO, удобная панель управления собственной разработки и посуточная оплата.

Источник

Оцените статью