Diary?::2006-04-05

20:58

がああああああああああああああああああああ、クロージャも高階関数も何もない言語ばかり触っていると気がおかしくなる。

22:01

今日は帰りの電車でまるまる一時間睡眠が取れたので、月曜日よりは多少頭が冴えてる。

そうれはそうと、 C/C++ という表記と同程度には、 Perl/Python/Ruby/PHP などと表記するのも正しくないのだよな。これは俺も使ってしまう時があるような気がするので、今後はこれも封印しよう。

22:54

とりあえずメタクラスを用いた契約をやってみた。

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__ をフックするのが一番か。

Written by Kuwata Chikara
Creative Commons