Clojure Jython interop - PullRequest
       10

Clojure Jython interop

13 голосов
/ 25 января 2010

Мне было интересно, пытался ли кто-нибудь как-нибудь вызвать функции Jython изнутри Clojure, и как ты это сделал, если так. я имею не используется Jython, но я думаю, что интерпретатор Jython может быть вызывается так же, как и любой другой код Java, и программы на Python может быть запущен в нем. Однако мне интересно, если бы было возможно как-то вызвать отдельные функции Python из Clojure. Как я уже сказал, я еще не пробовал, так что это может быть просто и очевидно. Мне просто интересно, пытался ли кто-нибудь сделать это.

Спасибо, Rob

1 Ответ

13 голосов
/ 25 января 2010

Примечание: Я только что понял, что вопрос конкретно о вызове функций Jython из Clojure, а , а не о создании полноценного решения взаимодействия Jython-Clojure ... Но! Я уже подготовил небольшую рецензию на мои первые мысли о последнем, и я думаю, что в любом случае это логичный следующий шаг. Я имею в виду, как вы можете использовать интересные пакеты Python без достаточно удобного доступа к классам Python? Написание функций Python для переноса вызовов методов и т. П. - возможная идея ... но довольно ужасная. Так что в любом случае.

Для базового вызова Jython-выполняемых функций Python из Clojure прочитайте второй абзац ниже этого пункта и фрагменты кода. Тогда прочитайте остальное для удовольствия и непредвиденной прибыли.

Я думаю, что опыт изначально был бы далеко не без шва ... На самом деле, я предсказывал, что сглаживание ударов может быть действительно королевской болью. Тем не менее, у меня есть догадка, что на самом деле это может быть проще, чем звонить в Jython из Java. Просто длинная 0,02 евро от меня ... Пусть кто-нибудь более знающий придет и покажет мне, что я не знаю, о чем говорю. ; -)

Первое, на что нужно обратить внимание, это то, что Jython включает все в свои собственные классы, все происходящие из org.python.core.PyObject, не заботится о том, чтобы Python вызывал Callable или Runnable и т. Д. на самом деле, это не будет большой проблемой для некоторых мультиметодных / макропакетов.

Классы Python можно использовать из Java, но мое (возможно, ошибочное) понимание состоит в том, что обычно при попытке воздействовать на экземпляры классов Python, созданные в Jython, код Java видит только методы, унаследованные от базового класса или интерфейса Java. .. В противном случае требуется специально отформатированная строка документации (!). Вот ссылка на соответствующую страницу в JythonWiki. (Понятия не имею, насколько он актуален.) Круто, очевидно, PyObjectDerived (экземпляр пользовательского класса Python) можно убедить вызвать его методы с заданными аргументами. Поэтому, прилежно стараясь обернуть, можно надеяться, что для этого можно будет использовать несколько сносный синтаксис.

На самом деле, давайте посмотрим код:

;; a handy instance of PythonInterpreter...
(def python (org.python.util.PythonInterpreter.))
(.eval python "5")
; -> #<PyInteger 5>

Ну, все обернуто. Веселая Clojuresque распаковщик:

(defmulti py-wrap class)
;; but let's not wrap if already a PyObject...
(defmethod py-wrap org.python.core.PyObject [pyo] pyo)
(defmethod py-wrap Integer [n] (org.python.core.PyInteger n))
(defmethod py-wrap Long [n] (org.python.core.PyLong n))
(defmethod py-wrap BigInteger [n] (org.python.core.PyLong n))
(defmethod py-wrap String [s] (org.python.core.PyString s))

И аналог выше:

(defmulti py-unwrap class)
;; if unsure, hope it's not a PyObject at all...
(defmethod py-unwrap :default [x] x)
(defmethod py-unwrap org.python.core.PyInteger [n] (.getValue n))
(defmethod py-unwrap org.python.core.PyString [s] (.toString s))

Функции: вы можете .__call__ их и вы можете ._jcall их. Последний вариант несколько более приятен, так как он принимает массив обычных Java-объектов Java, хотя все равно возвращает PyObject. Первый принимает соответствующее количество позиционных аргументов, которое должно быть уже на PyObject с. Я понятия не имею, как передать аргументы ключевых слов ... хотя Jython так или иначе делает это, поэтому должен быть способ.

Вот ультраосновный помощник для вызовов ._jcall:

(defn py-call [pyf & args]
  (apply (fn [pyf & args] (._jcall pyf (into-array args)))
         (map #(if (string? %) (py-eval %) %)
              (cons pyf args)))

Вы можете .exec строку, содержащую определение Python fact, затем сделать (py-call "fact" 10), чтобы получить #<PyInteger 5> назад; разверните, если вам так хочется.

И так далее, и тому подобное ... Чего я не знаю, так это:

  1. Какие усилия потребуются для того, чтобы сделать это достаточно полезным, чтобы связать интересный код Clojure с интересным кодом Python.
  2. Если для обеспечения разумного синтаксиса вызовов в Python потребуется что-то действительно плохое для производительности.
...