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