Подтвердить что ты не робот

Clojure Jython interop

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

Спасибо, Rob

4b9b3361

Ответ 1

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

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

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

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

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

Фактически, посмотрим на некоторый код:

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

Ну, все обернуто. Веселый Clojuresque unrapper:

(defmulti py-wrap class)
;; but let 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 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 s. Я не знаю, как передать аргументы ключевого слова... хотя Jython делает это как-то, поэтому должен быть способ.

Здесь супер-базовый помощник для ._jcall -type вызывает:

(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> назад; разверните, если вам это нравится.

И так далее и т.д. Что я не знаю:

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