Closed Daniel-Alievsky closed 2 years ago
PyObject is the easiest way to access python object fields from java. PyCallable can be used to call methods:
try (SharedInterpreter interpreter = new SharedInterpreter()) {
interpreter.exec("class Simple:\n" +
" def test(self):\n" +
" return 123;");
PyCallable simple = interpreter.getValue("Simple", PyCallable.class);
PyObject instance = simple.callAs(PyObject.class);
Number result = instance.getAttr("test", PyCallable.class).callAs(Number.class);
System.out.println(result);
}
You can also use the proxy capabilities of PyObject to get a more friendly java interface for you python objects:
public interface Simple {
public int test();
}
try (SharedInterpreter interpreter = new SharedInterpreter()) {
interpreter.exec("class Simple:\n" +
" def test(self):\n" +
" return 123;");
PyCallable simple = interpreter.getValue("Simple", PyCallable.class);
PyObject instance = simple.callAs(PyObject.class);
Simple instanceJava = instance.proxy(Simple.class);
int result = instanceJava.test();
System.out.println(result);
}
Thank you, I understand. Looks like I just didn't notice those classes :) Maybe you will add some reference to them into https://github.com/ninia/jep/wiki/Getting-Started ?
Ok, I understand how to create and access an instance of some class, which is declared in Python code. In your example it is "Simple" class. However, I'd prefer not to create any new entities from Java code without necessity: all users of this interpreter will see new class "Simple". Could you advise, maybe there is a good way to create an "anonymous" object, containing some set of fields, and pass it to some Python function?
I'd like to implement something like this. I have a user's Python code:
def customFunc(params):
some processing params
It is supposed that params contain some parameters, for example, params.a, params.b, etc., filled from Java side and analysed in Python code. Can I create, in Java, some instance of object "params" without declaring any special Python classes, then add there some fields (numbers, strings, ND-arrays), and then call "customFunc" with this object via "invoke" method?
Sorry for too simple question, Python is a new area for me.
Also, I see method "getAttr", but don't see method like "hasAttr", allowing the check whether an object contains the attribute. How can I get the list of all attributes in Java?
It sounds like your python function should be written to take arbitrary keyword arguments. Both PyCallable.call and Interpreter.invoke are overloaded to take a Map<String,Object> that will be used for the keyword args.
If you cannot use kwargs or Map you could also define a custom java class with fields a, b, etc... and set those fields. When that Java object is passed into a python function the fields can be accessed like ordinary python attributes.
To get a list of attributes for a python object you can use the python builtin dir() function.
How can I call standard functions like dir, hasattr, str, ... from Java?
I have an instance python object as "PyObject obj".
obj.getAttr("__dir__", PyCallable.class).call();
works fine: it returns a list of attributes.
interp.invoke("dir", obj)
doesn't work at all, an exception:
Unable to find object with name: dir
Moreover, I tried to call other standard functions like str(), print(), etc. - "invoke" method does not recognize them. What is the reason?
It sounds like your python function should be written to take arbitrary keyword arguments. Both PyCallable.call and Interpreter.invoke are overloaded to take a Map<String,Object> that will be used for the keyword args.
No, my desire was little another. I'm working over Java system (named Stare), where the user can write his own Python code (in a little textbox), and I will execute it via JEP. But what is "Python code"? I speak about something useful for commercial project, written according some simple standard. In other words, it should be a function with some simple name, that has some parameters and returns some information. But different users need different sets of parameters, different results. These sets may be configured in different ways; in particular, I offer user to fill some JSON, where all parameters and results are specified in some simple form.
How can it be implemented on Python side? My idea is to offer the user to write a function with maximally simple syntax:
def customFunc(params):
# we have, for example, params.price, params.amount, params.matrix_to_process (numpy)
# some processing params
# create result object - maybe instance of some class
result.total = something
result.smoothed_matrix = ...
return result
Set of attributes in params and result are specified by JSON, provided by user.
How can I create "params" from Java? I know only one way: I should declare, before interpreting this code, some special python class, for example:
class StareParams:
pass
Then, really, I can create its instance (as you described) and then add any necessary attributes according JSON model, for example,
obj.setAttr("price", 314.2);
Sounds good, and probably I will use this way. But here is only one little problem: I need to add special class StareParams, than will be visible inside user's Python code. What will be if he will declare class StareParams himself??
Of course, I'd prefer to use some standard, already existing class, allowing to add any fields. In JavaScript I could use something like "new Object()". Unfortunately, Python analog "object()" does not allow to add fields dynamically, because it has no __dict__
attribute. I can create an instance of standard dict object:
interp.getValue("dict", PyCallable.class).callAs(PyObject.class);
But it will be no so convenient on Python side...
It seems here is not better solution, than create special class with standard name like StareParams and warn the user that it is reserved standard name.
Moreover, I tried to call other standard functions like str(), print(), etc. - "invoke" method does not recognize them. What is the reason?
You will need to import builtins so that the dir method will be available in the globals. You can do from builtins import dir
or just import builtins' and then invoke with
builtins.dirinstead of
dir`
Thank you for your explanations!
Usually Python program works with objects, their properties and methods. Of course, global variables and functions are also possible, but it is not a good style for OOP code.
But I didn't find in your library a standard way to manipulate fields/methods from Java. I didn't found functions, allowing to create a new Python object (for example, an instance of Python class), to read/write fields of the given object, to call some method of an object. Probably it is possible via "exec" method, but it is not obvious for me, how to do this in some cases.
For example, if some Python function returned an object "x" with field "x.a" - x returned as an Object result of your invoke method - how can I read its "a" property?
Moreover, let's declare some simple class in Python:
How can I create, from java, an instance of this class and, then, to call "test" method? It is already not a simple function, but a method.
It seems it could be a good idea to add such abilities as a standard methods of the Interpreter interface.