がああああああああああああああああああああ、クロージャも高階関数も何もない言語ばかり触っていると気がおかしくなる。
今日は帰りの電車でまるまる一時間睡眠が取れたので、月曜日よりは多少頭が冴えてる。
そうれはそうと、 C/C++ という表記と同程度には、 Perl/Python/Ruby/PHP などと表記するのも正しくないのだよな。これは俺も使ってしまう時があるような気がするので、今後はこれも封印しよう。
とりあえずメタクラスを用いた契約をやってみた。
def dbc_factory(name, base, namespace):
klass = type(name, base, namespace)
class Contractor(klass, object):
def __init__(self, *args, **kwds):
klass.__init__(self, *args, **kwds)
for k, v in klass.__dict__.iteritems():
if hasattr(v, '__call__'):
self.__set_dbc(k)
def __set_dbc(self, name):
f = getattr(self, name)
if hasattr(f, '__require__') and hasattr(f, '__ensure__'):
setattr(self, f.__name__, self._dbc(f))
return
def _dbc(self, func):
def __dbc(*args, **kwds):
func.__require__(self, *args, **kwds)
r = func(self, *args, **kwds)
func.__ensure__(self, r)
__dbc.__name__ = func.__name__
__dbc.__doc__ = func.__doc__
return __dbc
Contractor.__name__ = name
return Contractor
class Hoge:
__metaclass__ = dbc_factory
def __init__(self):
self.hoge = 'hogeeeeeeee!'
def _pre_f(self, *args):
assert not 0 in args
def _after_f(self, r):
assert r is None
def f(self, *args):
print args
f.__require__ = _pre_f
f.__ensure__ = _after_f
h = Hoge()
h.f(0,1,2,3)
インスタンスメソッドに __require__ と __ensure__ というスロットを拡張して、そこに事前事後のチェックメソッドをそのまま格納している。サンプルのクラスにやる気が感じられないのはともかくとして、これは前に書いた奴よりはだいぶ使えそうな気がする。というか、 decorator を使う奴は普通の関数に、インスタンスのメソッドにはメタクラスと使い分ければ良いんだろうけど。
しかし未だに契約のメリットがわかってないんだよなあ。これはテストとは直行する概念なのか? なんとなくだけど、これは OffensiveProgramming に通じる部分がある気がする。
あともう少しこれを拡張すれば、クラス不変条件も定義できるか。その場合は全てのメソッドでチェックか? そんなバカな。副作用のあるメソッドだけで十分だと思うが、じゃあ副作用のある無しはどうやって外部から見るんだ? オーバーヘッドがどうなるか心配だが、 __setattr__ をフックするのが一番か。