juliaの型でpythonオブジェクトをラップするときのテクニック
いきなりだが、juliaでpythonパッケージをpyimportしてラップしたものを使う際に、hoge.pyobj~のように間にpyobjをうたないといけない。
本記事ではpyobjの入力を省略するテクニックを紹介する。
通常の場合
using PyCall
numpy = pyimport("numpy")
struct Hoge
pyobj::PyObject
end
これを実行すると
hoge = Hoge(numpy)
hoge.pyobj.sqrt(2) #この例のようにいちいちpyobjを打たないといけない
これを以下のようにpyobjを省いて実行するのが本記事のゴールである。
hoge.sqrt(2)
コードは以下のようになる。
using PyCall
numpy = pyimport("numpy")
struct Hoge
pyobj::PyObject
end
function Base.getproperty(h::Hoge, s::Symbol) #構造体を引数に保つ関数!
if s == :pyobj
getfield(h, s)
else
getproperty(getfield(h, :pyobj), s)
end
end
実行
hoge = Hoge(numpy)
# Case1
hoge.sqrt(2) #1.4142135623730951
#Case2
hoge.pyobj.sqrt(2) #上と同じ結果
何が起こっているのか
Case1の場合
結論から言うと、hoge.sqrtは、getproperty(hoge, :sqrt) と同じである(言い換えると、hoge.sqrt(2)は、getproperty(hoge, :sqrt)(2)と同じ)。
まず、hoge.sqrtが実行されると内部でBase.getproperty関数が呼び出される。Base.getpropertyの引数には、それぞれ第一引数hにhoge、第二引数sにsqrtが対応する。
s == :pyobjはFalseなので、getpropertyが呼びだれることになる。
ここで、getfield(h, :pyobj) は、Hogeのフィールドのpyobj,ここではnumpyに対応する。
(getfield(hoge, :pyobj)を実行するとわかります。)
従って、getproperty(getfield(h, :pyobj), s)とは、numpy.sqrtを実行していることになる。
Case2の場合
hoge.pyobjでBase.getproperty関数が呼び出される。Base.getpropertyの引数には、それぞれ第一引数にhoge、第二引数にpyobjが対応する。
s == :pyobjがTrueなので、getfield(h, s)が実行される。