Jython, использовать только метод из Python из Java?
При чтении и использовании этой статьи предполагается, что у нас есть полное определение объекта с объектами класса и сопоставления (прокси) от python до java.
Можно ли импортировать только метод (не определенный внутри класса, но использующий внутренний класс python) из фрагмента кода в python, не заключая его в определение класса (без использования описанной выше фабричной парадигмы).
Я хотел бы сделать что-то from myPyFile import myMethod
из Java, а затем использовать myMethod, непосредственно из Java (может быть, в качестве статического метода?)? Но если это возможно, я не нашел никакой подсказки о том, как это сделать (интерфейс, описанный в статье, все еще может быть необходим, чтобы сказать Java, как использовать myMethod?)
С наилучшими пожеланиями.
РЕДАКТИРОВАТЬ: я сейчас имею дело с Jython 2.5.2, так что это может быть в зависимости от версии и гораздо проще в будущем?
РЕДАКТИРОВАТЬ: Ниже в ответ на Даниэль:
Вот пример кода, чтобы воспроизвести полученную ошибку, а также получить рабочий пример из вашего полезного ответа!
(Ну и добавим немного другой вопрос по отображению обратно в Java-объекты из результата Python/Jython сдоходностью)
(@Joonas, извините, я изменил свой код, и теперь я не могу вернуться к ошибке, которая у меня была раньше)
import org.python.core.Py;
import org.python.core.PyList;
import org.python.core.PyTuple;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PySystemState;
import org.python.util.PythonInterpreter;
interface MyInterface {
public PyList getSomething(String content, String glue, boolean bool);
}
class MyFactory {
@SuppressWarnings("static-access")
public MyFactory() {
String cmd = "from mymodule import MyClass";
PythonInterpreter interpreter = new PythonInterpreter(null, new PySystemState());
PySystemState sys = Py.getSystemState();
sys.path.append(new PyString("C:/jython2.5.2/Lib"));
interpreter.exec(cmd);
jyObjClass = interpreter.get("MyClass");
}
public MyInterface createMe() {
PyObject myObj = jyObjClass.__call__();
return (MyInterface)myObj.__tojava__(MyInterface.class);
}
private PyObject jyObjClass;
}
public class Main {
public static void main(String[] args) {
/*
// with only :
PythonInterpreter interpreter = new PythonInterpreter();
i get :
Exception in thread "main" Traceback (most recent call last):
File "<string>", line 1, in <module>
LookupError: no codec search functions registered: can't find encoding 'iso8859_1'
which is probably due to the :
#!/usr/bin/env python
# -*- coding: latin-1 -*-
// yes i am from France, so my - sorry for that - bad english ;) and have to deal with non 127 ascii chars :)
*/
PythonInterpreter interpreter = new PythonInterpreter(null, new PySystemState());
PySystemState sys = Py.getSystemState();
sys.path.append(new PyString("C:/jython2.5.2/Lib"));
interpreter.exec("from mymodule import getSomething");
PyObject tmpFunction = interpreter.get("getSomething");
System.err.println(tmpFunction.getClass());
MyInterface i = (MyInterface) tmpFunction.__tojava__(MyInterface.class);
System.err.println(i.getSomething("test", " - ", true));
for (Object x : i.getSomething("test", " - ", true)) {
System.out.println(x);
// How can i get back an equivallent of the Python _"for (a, b) in getSomething:"_
// with _"a"_ being PyUnicode or better String, and _"b"_ being boolean ?
}
// ^^ so adapting Daniel Teply solution works ! Thanks to him...
// BTW the part below did not work : but i may have missed or/and also mixed many things :/
// i feel Jython damned hard to dive in :/
// and really hope that the sample that i post and answers that i get will help others to swim!
try {
MyFactory factory = new MyFactory();
MyInterface myobj = factory.createMe();
PyList myResult = myobj.getSomething("test", " - ", true);
System.out.println(myResult);
}
catch (Exception e) {
System.out.println(e);
// produce a : java.lang.ClassCastException: org.python.core.PySingleton cannot be cast to MyInterface
// EDIT : see below last edit, this error may be due to my forgotten heritage from interface in the python code!
}
System.exit(-1);
}
}
Часть Python: (mymodule.py)
#!/usr/bin/env python
# -*- coding: latin-1 -*-
class MyClass:
def __init__(selfself):
pass
def getSomething(self, content, glue = '', bool = True):
for x in range(5):
yield (glue.join(map(str, (content, x, chr(97 + x))))), bool
#return list()
def getSomething(content, glue = '', bool = True):
print "test"
myclass = MyClass()
return list(myclass.getSomething(content, glue, bool))
def main():
test()
if __name__ == "__main__":
main()
РЕДАКТИРОВАТЬ:
Частично отвечая себе на внутренний вопрос (внутри комментариев):
(на самом деле я чувствую, что мой ответ и код уродливы, но он работает, и, кажется, все в порядке, если я не расскажу, я не знаю, есть ли лучший Jythonic-способ сделать это, если это так, мне действительно интересно:))
for (Object x : i.getSomething("test", " - ", true)) {
System.out.println(x);
// How can i get back an equivallent of the Python _"for (a, b) in getSomething:"_
// with _"a"_ being PyUnicode or better String, and _"b"_ being boolean ?
// answering myself here :
PyTuple mytuple = (PyTuple) x; // casting back x as PyTuple, can we have a java equivalent to _`(a, b) = x_ ? not sure...
PyObject a = mytuple.__getitem__(0);
PyObject b = mytuple.__getitem__(1);
String aS = a.toString(); // mapping a unicode python string to java is as simple?
boolean bB = b.toString().toLowerCase().equals("true");
System.out.println(mytuple + "[" + aS + "][" + b + "][" + bB + "]");
РЕДАКТИРОВАТЬ:
Отвечая на часть о "производим: " java.lang.ClassCastException: org.python.core.PySingleton не может быть приведен к MyInterface "... большинство моих недоразумений и ошибок, связанных с тем, что я забыл обработать Java из части Python! (см. мой код выше, я оставляю это без исправления об этом факте, потому что это не был мой главный вопрос, и в его реальной форме, это рабочий ответ на этот главный вопрос, большое спасибо Дэниелу и Джунасу, которые помог мне понять). Поэтому для заводской парадигмы НЕ следует забывать добавить адекватный импорт в свой файл Python:
from testjython.interfaces import MyInterface #// defining method inside a MyInterface.java
class MyClass(MyInterface):
[...]
Еще одна трудность, с которой я столкнулся, заключалась в правильной обработке импорта и пакетов. Кстати, добавление этого кода в верхний код приведет к ошибке TypeError: невозможно преобразовать в org.python.core.PyList, но это еще одна проблема...
2 ответа
Ты можешь использовать PyObject.__call__(Object... args)
чтобы вызвать любой вызываемый объект Python. Вы можете получить функцию PyFunction, представляющую вашу функцию со стороны Java, так же, как в примере вы получаете класс сотрудника Python.
Альтернативно, вы можете скрыть это за единственным интерфейсом метода на стороне Java, вызвав __tojava__(Interface.class)
на функции, которую вы получили из интерпретатора Python. Подробный пример (на самом деле проверено!): Файл python:
def tmp():
return "some text"
Джава:
public interface I{
public String tmp();
}
public static void main(String[] args) {
PythonInterpreter interpreter = new PythonInterpreter();
interpreter.exec("from test import tmp");
PyObject tmpFunction = interpreter.get("tmp");
System.err.println(tmpFunction.getClass());
I i = (I) x.__tojava__(I.class);
System.err.println(i.tmp());
}
выход:
class org.python.core.PyFunction
some text
Импорт только метода невозможен, потому что в Java методы (или функции) не являются объектами первого класса, то есть нет способа ссылаться на метод без предварительной ссылки на некоторый класс (или интерфейс). Даже статические методы заключены в класс, и вы обращаетесь к ним через объект класса.
Однако вы можете довольно близко познакомиться с решением, представленным в Jython 2.5.2: функции Jython работают непосредственно как реализации Java-интерфейсов с одним абстрактным методом (см. http://www.zyasoft.com/pythoneering/2010/09/jython-2.5.2-beta-2-is-released/). Таким образом, вы можете определить интерфейс в Java - важно, чтобы он содержал ровно одно определение метода:
interface MyInterface {
int multiply(int x, int y);
}
Плюс что-то вроде этого в Jython:
myFunction = lambda x, y : x * y
и использовать это как MyInterface
на Яве. Вам все еще нужно использовать своего рода фабричный шаблон, как описано в статье, на которую вы ссылаетесь, чтобы получить функцию Jython для Java, но что-то вроде этого работает (возможно, содержит ошибки, но идея такова):
PyObject myFunction = interpreter.get("myFunction");
MyInterface myInterface = (MyInterface)myFunction.__tojava__(MyInterface.class);
int x = myInterface.multiply(2, 3); // Should return 6.