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.

    Dict.java

      package py4j_example;
    
      import java.util.HashMap;
    
      public class Dict {
        private HashMap<Integer, String> dict = 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");
        }
      }
    


    DictEntryPoint.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;
        }
      }
    


    Program.java

      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
    


    Application.py

      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, удобная панель управления собственной разработки и посуточная оплата.

    Подписывайтесь на наш чат в Telegram.

    VDSina.ru
    Серверы в Москве и Амстердаме

    Комментарии 4

      +5
      Здесь гораздо важнее было бы уточнить про время обмена объектами между python и java: это ооооочень медленный процесс.
      Если хочется быстрого и удобного взаимодействия я бы смотрел на грааль
        +1

        Есть еще PyJNIus (https://github.com/kivy/pyjnius) он работает через JNI, он удобнее и быстрее чем Py4J

          0

          Все-таки JPype удобнее.
          И быстрее, и код на питоне выглядит приличнее.

            0
            Аналог для .NET — Python.NET.

            Он больше похож на JPype или PyJNIus, т.к. использует не сокеты, а P/Invoke. Выгодно отличается от обоих (может уже нет, не слежу за Java) тем, что в C# есть встроенная поддержка динамичесих языков через dynamic.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое