Примечание: Я только что понял, что вопрос конкретно о вызове функций 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>
назад; разверните, если вам так хочется.
И так далее, и тому подобное ... Чего я не знаю, так это:
- Какие усилия потребуются для того, чтобы сделать это достаточно полезным, чтобы связать интересный код Clojure с интересным кодом Python.
- Если для обеспечения разумного синтаксиса вызовов в Python потребуется что-то действительно плохое для производительности.