Working with PyObjects
Calling Jython classes in Java, and writing Jython modules in Java requires extensive use of Jython's special methods. Special methods are those methods that begin and end with two underscores. Jython's flexible operation creates special needs for calling PyObjects from within Java.
Jython's dynamic operation means that the finding, getting, setting, and calling of attributes all happens through methods that provide opportunity to customize or extend each step. Two special methods often used from Java are __findattr__ and __finditem__. The __findattr__ method returns object attributes, and the __finditem__ method returns sequence or dictionary values designated by a key. Usages of these methods create the following signatures:
public PyObject __finditem__(PyObject key) public PyObject __finditem__(int index) public PyObject __finditem__(String key) public PyObject __findattr__(String name) public PyObject __findattr__(PyObject name)
The __finditem__ and __findattr__ methods that accept a String object as a parameter both require that the string object be interned. String literals are automatically interned, but otherwise, the string must be explicitly interned.
If you import the random module and intend to use the randint method, you cannot just call random.randint directly:
// This is not right PyObject random = __builtin__.__import__("random"); random.randint(new PyInteger(10), new PyInteger(100));
Instead, you should use the __findattr__ method combined with the __call__ method:
PyObject random = __builtin__.__import__("random"); random.__findattr__("randint").__call__(new PyInteger(10), new PyInteger(20));
The shortcut method invoke exists to help in calling a method on a PyObject from Java. Specifically, the correct and generic way to call methods on all kinds of Jython mapping objects (such as PyDictionary and PyStringMap) is with the invoke method. Calling the invoke method on a PyObject's method is the equivalent of calling the following:
myPyObject.__getattr__(name).__call__(args, keywords)
Following are the different method signatures of the invoke method:
public PyObject invoke(String name) public PyObject invoke(String name, PyObject arg1) public PyObject invoke(String name, PyObject arg1, PyObject arg2) public PyObject invoke(String name, PyObject[] args) public PyObject invoke(String name, PyObject[] args, String[] keywords)
The different signatures respectively allow for differing sets of arguments passed to the invoked method, but what does not differ is that the first argument is a string representing the name of the PyObject's method to call. This name parameter must be an interned string. Remember that string literals are automatically interned.
The following example shows the creation of a PyDictionary and the retrieval of its keys from within Java using the invoke method. This example uses PyDictionary's empty constructor and subsequently adds values with the __setitem__ special method:
PyDictionary dct = new PyDictionary(); dct.__setitem__(new PyString("G"), new PyInteger(1)); dct.__setitem__(new PyString("D"), new PyInteger(2)); dct.__setitem__(new PyString("A"), new PyInteger(3)); dct.__setitem__(new PyString("E"), new PyInteger(4)); dct.__setitem__(new PyString("B"), new PyInteger(5)); PyObject keys = dct.invoke("keys");
The invoke method above uses the keys method of the PyDictionary object to retrieve its keys. Because the method name (keys) is a string literal in the previous example, it is automatically interned.
Looping through the keys returned in the previous example also requires special care. Because a PyObject's __len__ method may return wrong values, the only way to safely loop through Jython sequences in Java is to test for each index value until reaching a non-existing index. If we continue the dictionary keys example from above with a loop through those keys, the proper and generic way to loop through them is implemented as follows:
PyObject key; for (int i = 0; (key = keys.__finditem__(i)) != null; i++) { System.out.println("K: " + key + ", V: " + dict.__getitem__(key)); }