オーストラリアで勉強してきたMLデザイナーの口語自由詩

主に、データ分析・機械学習・ベイズ・統計について自由に書く。

pymc4のソースコード読んでみた - “withparent”, Initial Model Class, sampling and random variable [aafa32d]

f:id:yukinagae:20171122095115p:plain

コミット

2018/05/30のコミットです。

Initial Model Class, sampling and random variable · pymc-devs/pymc4@aafa32d · GitHub

主に、pymc4の根幹となる ModelRandomVariable クラスが作成されています。

以下ファイルが追加されています。

  • .vscode/settings.json
  • pymc4/init.json
  • pymc4/model.py
  • pymc4/random_variable.py
  • pymc4/sample.py
  • test.ipynb

前回飛ばした withparent のところを継続して読んでみます。

withparent - model.py

withparent メソッドは以下の treedict クラスの内部で呼ばれています(メソッド内ではなく単体で呼ばれています)

親の dict クラスの __setitem__update メソッドを上書きしているように見えます。 withparent という名前からすると、値のセット・更新時に parent の値も拡張するようにしていると推測できます。

# typechecking here works bad
__setitem__ = withparent(dict.__setitem__)
update = withparent(dict.update)

実際に withparent メソッドを読んでみます。

def withparent(meth):
    """Helper wrapper that passes calls to parent's instance"""
    def wrapped(self, *args, **kwargs):
        res = meth(self, *args, **kwargs)
        if getattr(self, 'parent', None) is not None:
            getattr(self.parent, meth.__name__)(*args, **kwargs)
        return res
    # Unfortunately functools wrapper fails
    # when decorating built-in methods so we
    # need to fix that improper behaviour
    wrapped.__name__ = meth.__name__
    return wrapped

コメントに Helper wrapper that passes calls to parent's instance とあるように、 parentインスタンスを呼ぶヘルパーメソッドのようです。このあたりは以前に読んだ tree_contains と似たようなものだと思えます。

withparent 内の以下の wrapped メソッドが定義されているのでまずはこちらを読みます。 methwithparent から渡ってきた引数の dict.__setitem__dict.update になります。

def wrapped(self, *args, **kwargs):
    res = meth(self, *args, **kwargs)
    if getattr(self, 'parent', None) is not None:
        getattr(self.parent, meth.__name__)(*args, **kwargs)
    return res

以下の処理では methdict.__setitem__ と同等だとすると、単に self.__setitem__ を呼んでいるのと一緒のはずです。

res = meth(self, *args, **kwargs)

その後に tree_contains と同様に、 parent の有無をチェックしてもし存在するなら parent__setitem__ を呼び出しています。

if getattr(self, 'parent', None) is not None:
     getattr(self.parent, meth.__name__)(*args, **kwargs)

最後に wrapped メソッドの __name__ を上書きしています。以下のように試しに名前変更前後の値を見てみると、もともと wrapped という名前が __setitem__ という名前に変更されていることがわかります。

print(wrapped.__name__) # <= wrapped
wrapped.__name__ = meth.__name__
print(wrapped.__name__) # <= __setitem__
return wrapped

ちなみに def __setitem__def update を単に treedict クラス内で作成して、親の dict クラスの処理を上書きしても同様のことができるはずです。

References