Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

Jythonに組み込み関数を追加する

某blogのコメント欄
に書いてあったので。
まず関数編。
javaで作成した関数をjython側で組み込み関数として使ってみます。
JythonSample.java

import java.net.URL;
import org.python.core.PyString;
import org.python.util.PythonInterpreter;
import test.TestFunction;

/**
 * 
 * @author Yutaka Matsubara (゚∀゚)
 */
public class JythonSample {
    /**
     * 
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // Pythonファイルを検索する
        String fqcn = JythonSample.class.getName();
        String resourcePath = fqcn.replace('.', '/') + ".class";
        URL url = Thread.currentThread().getContextClassLoader().getResource(resourcePath);
        String s = url.toString();
        s = s.substring(6, s.lastIndexOf("/") + 1) + "builtin.py";
        PythonInterpreter pyi = new PythonInterpreter();
        
        // 属性を追加
        // Python上のObjectとして組み込む
        pyi.getLocals().__setitem__("test", new PyString("testStringですよー"));
        
        // 関数を属性として追加
        // 組み込み関数っぽく組み込む
        pyi.set("testFunc", new TestFunction());
        pyi.execfile(s);
    }
}

関数testFunc

package test;

import org.python.core.Py;
import org.python.core.PyBuiltinFunctionSet;
import org.python.core.PyObject;

/**
 * 受け取った値をコンソールに出力する関数です。
 * 
 * @author Yutaka Matsubara (゚∀゚)
 */
public class TestFunction extends PyBuiltinFunctionSet {
    /**
     * すんげーてきとー
     */
    public TestFunction() {
        super("testFunc", 999, 0, 1, false, null);
    }

    /**
     * 引数なしで呼ばれた場合はエラー
     */
    public PyObject __call__() {
        throw argCountError(0);
    }

    /**
     * 引数の値をtoStringしてコンソール出力します。
     * Objectの型はPyObject
     */
    public PyObject __call__(PyObject arg1) {
        System.out.println("test function called = " + arg1.toString());
        //Noneを返す
        return Py.None;
    }
    
}

ちょーてきとーです。
呼び出し側のbuiltin.py

# 全属性出力
print dir()

#属性 test
print type(test)
print test

#属性testFunction
print type(testFunc)
testFunc(test)
t = (1,2,3,4)
testFunc(t)
d = dir
testFunc(d)

実行結果

['__doc__', '__name__', 'test', 'testFunc']
<type 'str'>
testStringですよー
<type 'TestFunction'>
test function called = testStringですよー
test function called = (1, 2, 3, 4)
test function called = <java function dir 1>
終了

dir()で調べてみると両方追加されていますね。
typeを調べてみましたがてきとーな値が出ています。

testFunc(test)

一応一発で呼べますね。

もっと荒業するならばIntercepterにevalで先にいろんなことしておく手もありますね。
クラスの生成→関数を別属性に当てるとかすれば上のコード全く同じことできますね。

c = Class()
testFunc = c.TestFunc

こんな感じ。

組み込み関数を作ってみたが多分やり方的には何パターンかありそう。
このサンプルだと関数自体がクラスなので関数内にjavaクラスの参照も持てたりするので自由度が高いと思います。

あとビルトインオブジェクトか。
ビルトインオブジェクトに関してはあんましいらない気がする。
importを減らせるぐらいじゃないかなー?
上記の関数をコンストラクタとして扱いFactory的に使ってオブジェクトを返せばもっと自由度あがると思うし。

うくく。

追記
あ。勘違いかも。
組み込みオブジェクトが何を指しているかが微妙。
組み込み型を指しているのかな?
やりたいことってPyObjectの拡張かしら。
うーん。