SpecialistOff.NET / Вопросы / Статьи / Фрагменты кода / Резюме / Метки / Помощь / Файлы
НазадМетки: python
Note
Jython 2.5.0 does not support jythonc. Support is planned for 2.5.1. jythonc basically converted python source to java source, the replacement will generate bytecodes directly, and enable jython code to be imported directly into java (via generated proxies).
Jython can also create Java classes directly from your Jython code. This can produce very useful results, as you are then able to treat the results as if they are native Java classes, albeit with Python power under the hood.
To produce Java classes from Python code, Jython comes with a compiler called jythonc.
The process of creating Python classes that will produce Java classes is a bit more complex than when calling Java classes from Python, because the methods in Java classes are statically typed, while Python functions and methods are dynamically typed. Thus, you must somehow tell jythonc that a Python method is intended to have a particular set of argument types and that its return value is a particular type. You accomplish this with the @sigstring, which is placed right after the beginning of the Python method definition (this is the standard location for the Python documentation string). For example:
def returnArray(self):
    "@sig public java.lang.String[] returnArray()"
The Python definition doesn’t specify any return type, but the @sig string gives the full type information about what is being passed and returned. The jythonc compiler uses this information to generate the correct Java code.
There’s one other set of rules you must follow in order to get a successful compilation: you must inherit from a Java class or interface in your Python class (you do not need to specify the @sig signature for methods defined in the superclass/interface). If you do not do this, you won’t get your desired methods – unfortunately, jythonc gives you no warnings or errors in this case, but you won’t get what you want. If you don’t see what’s missing, it can be very frustrating.
In addition, you must import the appropriate java class and give the correct package specification. In the example below, java is imported so you must inherit from java.lang.Object, but you could also say from java.lang import Object and then you’d just inherit from Object without the package specification. Unfortunately, you don’t get any warnings or errors if you get this wrong, so you must be patient and keep trying.
Here is an example of a Python class created to produce a Java class. In this case, the Python file is used to build a Java .class file, so the class file is the desired target:
# Jython/PythonToJavaClass.py
# A Python class converted into a Java class
# Compile with:
# jythonc --package python.java.test PythonToJavaClass.py
from jarray import array
import java
class PythonToJavaClass(java.lang.Object):
    # The '@sig' signature string is used to create the
    # proper signature in the resulting Java code:
    def __init__(self):
        "@sig public PythonToJavaClass()"
        print("Constructor for PythonToJavaClass")
    def simple(self):
        "@sig public void simple()"
        print("simple()")
    # Returning values to Java:
    def returnString(self):
        "@sig public java.lang.String returnString()"
        return "howdy"
    # You must construct arrays to return along
    # with the type of the array:
    def returnArray(self):
        "@sig public java.lang.String[] returnArray()"
        test = [ "fee", "fi", "fo", "fum" ]
        return array(test, java.lang.String)
    def ints(self):
        "@sig public java.lang.Integer[] ints()"
        test = [ 1, 3, 5, 7, 11, 13, 17, 19, 23 ]
        return array(test, java.lang.Integer)
    def doubles(self):
        "@sig public java.lang.Double[] doubles()"
        test = [ 1, 3, 5, 7, 11, 13, 17, 19, 23 ]
        return array(test, java.lang.Double)
    # Passing arguments in from Java:
    def argIn1(self, a):
        "@sig public void argIn1(java.lang.String a)"
        print("a: %s" % a)
        print("a.__class__", a.__class__)
    def argIn2(self, a):
        "@sig public void argIn1(java.lang.Integer a)"
        print("a + 100: %d" % (a + 100))
        print("a.__class__", a.__class__)
    def argIn3(self, a):
        "@sig public void argIn3(java.util.List a)"
        print("received List:", a, a.__class__)
        print("element type:", a[0].__class__)
        print("a[3] + a[5]:", a[5] + a[7])
        #! print("a[2:5]:", a[2:5]) # Doesn't work
    def argIn4(self, a):
        "@sig public void \
           argIn4(org.python.core.PyArray a)"
        print("received type:", a.__class__)
        print("a: ", a)
        print("element type:", a[0].__class__)
        print("a[3] + a[5]:", a[5] + a[7])
        print("a[2:5]:", a[2:5] # A real Python array)
    # A map must be passed in as a PyDictionary:
    def argIn5(self, m):
        "@sig public void \
           argIn5(org.python.core.PyDictionary m)"
        print("received Map: ", m, m.__class__)
        print("m['3']:", m['3'])
        for x in m.keys():
            print(x, m[x])
First note that PythonToJavaClass is inherited from java.lang.Object; if you don’t do this you will quietly get a Java class without the right signatures. You are not required to inherit from Object; any other Java class will do.
This class is designed to demonstrate different arguments and return values, to provide you with enough examples that you’ll be able to easily create your own signature strings. The first three of these are fairly self-explanatory, but note the full qualification of the Java name in the signature string.
In returnArray(), a Python array must be returned as a Java array. To do this, the Jython array() function (from the jarray module) must be used, along with the type of the class for the resulting array. Any time you need to return an array to Java, you must use array(), as seen in the methods ints() anddoubles().
The last methods show how to pass arguments in from Java. Basic types happen automatically as long as you specify them in the @sig string, but you must use objects and you cannot pass in primitives (that is, primitives must be ensconced in wrapper objects, such as Integer).
In argIn3(), you can see that a Java List is transparently converted to something that behaves just like a Python array, but is not a true array because you cannot take a slice from it. If you want a true Python array, then you must create and pass a PyArray as in argIn4(), where the slice is successful. Similarly, a Java Map must come in as a PyDictionary in order to be treated as a Python dictionary.
Here is the Java program to exercise the Java classes produced by the above Python code. You can’t compile TestPythonToJavaClass.java until PythonToJavaClass.class is available:
// Jython/TestPythonToJavaClass.java
import java.lang.reflect.*;
import java.util.*;
import org.python.core.*;
import java.util.*;
import net.mindview.python.*;
// The package with the Python-generated classes:
import python.java.test.*;
public class TestPythonToJavaClass {
  PythonToJavaClass p2j = new PythonToJavaClass();
  public void testDumpClassInfo() {
    System.out.println(
      Arrays.toString(
        p2j.getClass().getConstructors()));
    Method[] methods = p2j.getClass().getMethods();
    for(int i = 0; i < methods.length; i++) {
      String nm = methods[i].toString();
      if(nm.indexOf("PythonToJavaClass") != -1)
        System.out.println(nm);
    }
  }
  public static void main(String[] args) {
    p2j.simple();
    System.out.println(p2j.returnString());
    System.out.println(
      Arrays.toString(p2j.returnArray()));
    System.out.println(
      Arrays.toString(p2j.ints());
    System.out.println(
      Arrays.toString(p2j.doubles()));
    p2j.argIn1("Testing argIn1()");
    p2j.argIn2(new Integer(47));
    ArrayList a = new ArrayList();
    for(int i = 0; i < 10; i++)
      a.add(new Integer(i));
    p2j.argIn3(a);
    p2j.argIn4(
      new PyArray(Integer.class, a.toArray()));
    Map m = new HashMap();
    for(int i = 0; i < 10; i++)
      m.put("" + i, new Float(i));
    p2j.argIn5(PyUtil.toPyDictionary(m));
  }
}
For Python support, you’ll usually only need to import the classes in org.python.core. Everything else in the above example is fairly straightforward, as PythonToJavaClass appears, from the Java side, to be just another Java class. dumpClassInfo() uses reflection to verify that the method signatures specified in PythonToJavaClass.py have come through properly.