SpecialistOff.NET / Вопросы / Статьи / Фрагменты кода / Резюме / Метки / Помощь / Файлы
НазадМетки: python
To inject data into your Python program, the PythonInterpreter class has a deceptively simple method: set(). However, set() takes many different data types and performs conversions upon them. The following example is a reasonably thorough exercise of the various set() possibilities, along with comments that should give a fairly complete explanation:
// Jython/PythonInterpreterSetting.java
// Passing data from Java to python when using
// the PythonInterpreter object.
import org.python.util.PythonInterpreter;
import org.python.core.*;
import java.util.*;
public class PythonInterpreterSetting {
  public static void main(String[] args) throws PyException  {
    PythonInterpreter interp = new PythonInterpreter();
    // It automatically converts Strings
    // into native Python strings:
    interp.set("a", "This is a test");
    interp.exec("print(a)");
    interp.exec("print(a[5:])"); // A slice
    // It also knows what to do with arrays:
    String[] s = { "How", "Do", "You", "Do?" };
    interp.set("b", s);
    interp.exec("for x in b: print(x[0], x)");
    // set() only takes Objects, so it can't
    // figure out primitives. Instead,
    // you have to use wrappers:
    interp.set("c", new PyInteger(1));
    interp.set("d", new PyFloat(2.2));
    interp.exec("print(c + d)");
    // You can also use Java's object wrappers:
    interp.set("c", new Integer(9));
    interp.set("d", new Float(3.14));
    interp.exec("print(c + d)");
    // Define a Python function to print arrays:
    interp.exec(
      "def prt(x): \n" +
      "  print(x)\n" +
      "  for i in x: \n" +
      "    print(i,)\n" +
      "  print(x.__class__)\n");
    // Arrays are Objects, so it has no trouble
    // figuring out the types contained in arrays:
    Object[] types = {
      new boolean[]{ true, false, false, true },
      new char[]{ 'a', 'b', 'c', 'd' },
      new byte[]{ 1, 2, 3, 4 },
      new int[]{ 10, 20, 30, 40 },
      new long[]{ 100, 200, 300, 400 },
      new float[]{ 1.1f, 2.2f, 3.3f, 4.4f },
      new double[]{ 1.1, 2.2, 3.3, 4.4 },
    };
    for(int i = 0; i < types.length; i++) {
      interp.set("e", types[i]);
      interp.exec("prt(e)");
    }
    // It uses toString() to print Java objects:
    interp.set("f", new Date());
    interp.exec("print(f)");
    // You can pass it a List
    // and index into it...
    List x = new ArrayList();
    for(int i = 0; i < 10; i++)
        x.add(new Integer(i * 10));
    interp.set("g", x);
    interp.exec("print(g)");
    interp.exec("print(g[1])");
    // ... But it's not quite smart enough
    // to treat it as a Python array:
    interp.exec("print(g.__class__)");
    // interp.exec("print(g[5:])"); // Fails
    // must extract the Java array:
    System.out.println("ArrayList to array:");
    interp.set("h", x.toArray());
    interp.exec("print(h.__class__)");
    interp.exec("print(h[5:])");
    // Passing in a Map:
    Map m = new HashMap();
    m.put(new Integer(1), new Character('a'));
    m.put(new Integer(3), new Character('b'));
    m.put(new Integer(5), new Character('c'));
    m.put(new Integer(7), new Character('d'));
    m.put(new Integer(11), new Character('e'));
    System.out.println("m: " + m);
    interp.set("m", m);
    interp.exec("print(m, m.__class__," +
      "m[1], m[1].__class__)");
    // Not a Python dictionary, so this fails:
    //! interp.exec("for x in m.keys():" +
    //!   "print(x, m[x])");
    // To convert a Map to a Python dictionary, use PyUtil:
    interp.set("m", PyUtil.toPyDictionary(m));
    interp.exec("print(m, m.__class__, " +
      "m[1], m[1].__class__)");
    interp.exec("for x in m.keys():print(x,m[x])");
  }
}
As usual with Java, the distinction between real objects and primitive types causes trouble. In general, if you pass a regular object to set(), it knows what to do with it, but if you want to pass in a primitive you must perform a conversion. One way to do this is to create a “Py” type, such as PyInteger or PyFloat. but it turns out you can also use Java’s own object wrappers like Integer and Float, which is probably going to be a lot easier to remember.
Early in the program you’ll see an exec() containing the Python statement:
print(a[5:])
The colon inside the indexing statement indicates a Python slice, which produces a range of elements from the original array. In this case, it produces an array containing the elements from number 5 until the end of the array. You could also say ‘a[3:5]‘ to produce elements 3 through 5, or ‘a[:5]‘ to produce the elements zero through 5. The reason a slice is used in this statement is to make sure that the Java String has really been converted to a Python string, which can also be treated as an array of characters.
You can see that it’s possible, using exec(), to create a Python function (although it’s a bit awkward). The prt() function prints the whole array, and then (to make sure it’s a real Python array), iterates through each element of the array and prints it. Finally, it prints the class of the array, so we can see what conversion has taken place (Python not only has run-time type information, it also has the equivalent of Java reflection). The prt() function is used to print arrays that come from each of the Java primitive types.
Although a Java ArrayList does pass into the interpreter using set(), and you can index into it as if it were an array, trying to create a slice fails. To completely convert it into an array, one approach is to simply extract a Java array using toArray(), and pass that in. The set() method converts it to a PyArray – one of the classes provided with Jython – which can be treated as a Python array (you can also explicitly create a PyArray, but this seems unnecessary).
Finally, a Map is created and passed directly into the interpreter. While it is possible to do simple things like index into the resulting object, it’s not a real Python dictionary so you can’t (for example) call the keys() method. There is no straightforward way to convert a Java Map into a Python dictionary, and so I wrote a utility called toPyDictionary() and made it a static method of net.mindview.python.PyUtil. This also includes utilities to extract a Python array into a Java List, and a Python dictionary into a Java Map:
// Jython/PyUtil.java
// PythonInterpreter utilities
import org.python.util.PythonInterpreter;
import org.python.core.*;
import java.util.*;
public class PyUtil {
  /** Extract a Python tuple or array into a Java
  List (which can be converted into other kinds
  of lists and sets inside Java).
  @param interp The Python interpreter object
  @param pyName The id of the python list object
  */
  public static List
  toList(PythonInterpreter interp, String pyName){
    return new ArrayList(Arrays.asList(
      (Object[])interp.get(
        pyName, Object[].class)));
  }
  /** Extract a Python dictionary into a Java Map
  @param interp The Python interpreter object
  @param pyName The id of the python dictionary
  */
  public static Map
  toMap(PythonInterpreter interp, String pyName){
    PyList pa = ((PyDictionary)interp.get(
      pyName)).items();
    Map map = new HashMap();
    while(pa.__len__() != 0) {
      PyTuple po = (PyTuple)pa.pop();
      Object first = po.__finditem__(0)
        .__tojava__(Object.class);
      Object second = po.__finditem__(1)
        .__tojava__(Object.class);
      map.put(first, second);
    }
    return map;
  }
  /** Turn a Java Map into a PyDictionary,
  suitable for placing into a PythonInterpreter
  @param map The Java Map object
  */
  public static PyDictionary toPyDictionary(Map map) {
    Map m = new HashMap();
    Iterator it = map.entrySet().iterator();
    while(it.hasNext()) {
      Map.Entry e = (Map.Entry)it.next();
      m.put(Py.java2py(e.getKey()),
        Py.java2py(e.getValue()));
    }
    return new PyDictionary(m);
  }
}
Here is the unit testing code:
// Jython/TestPyUtil.java
import org.python.util.PythonInterpreter;
import java.util.*;
public class TestPyUtil {
  PythonInterpreter pi = new PythonInterpreter();
  public void test1() {
    pi.exec("tup=('fee','fi','fo','fum','fi')");
    List lst = PyUtil.toList(pi, "tup");
    System.out.println(lst);
    System.out.println(new HashSet(lst));
  }
  public void test2() {
    pi.exec("ints=[1,3,5,7,9,11,13,17,19]");
    List lst = PyUtil.toList(pi, "ints");
    System.out.println(lst);
  }
  public void test3() {
    pi.exec("dict = { 1 : 'a', 3 : 'b', " +
      "5 : 'c', 9 : 'd', 11 : 'e'}");
    Map mp = PyUtil.toMap(pi, "dict");
    System.out.println(mp);
  }
  public void test4() {
    Map m = new HashMap();
    m.put("twas", new Integer(11));
    m.put("brillig", new Integer(27));
    m.put("and", new Integer(47));
    m.put("the", new Integer(42));
    m.put("slithy", new Integer(33));
    m.put("toves", new Integer(55));
    System.out.println(m);
    pi.set("m", PyUtil.toPyDictionary(m));
    pi.exec("print(m)");
    pi.exec("print(m['slithy'])");
  }
  public static void main(String args[]) {
    TestPyUtil test = new TestPyUtil();
    test.test1();
    test.test2();
    test.test3();
    test.test4();
  }
}
We’ll see the use of the extraction tools in the next section.