エラーでなくワーニング (2)
- jijixi's diary: エラーでなくワーニング (http://jijixi.azito.com/cgi-bin/diary/index.rb?date=20071128#p03)
なるほど。しかし、Rubyのやり方でもHashクラスを書き直しているので他の部分に影響してしまうと思う。結局、使ったら元に戻しておかないといけないと思う。クラスを変更するのではなくインスタンスのみに変更を与えられれば良いのだけれども。Rubyなら特異メソッドは得意なのでできそうではあるがRubyは素人なので分からない…。
書き忘れていたかもしれないが、やりたいことは全てのインスタンスではなく、特定のインスタンスの辞書に対してのみ例外をフックしたい。
jijixiさんのPython版の実装は以下のように書けばすっきりする。ちなみにPythonの関数はreturnのみや何も書かない場合はNoneを返してくれるのでreturn Noneは不要。
class MyDict(dict): def __getitem__(self, key): if key not in self: print 'key not found' else: return dict.__getitem__(self, key)
new.instancemethodを使って辞書のインスタンスオブジェクトに独自定義の__getitem__を追加する方法を試してみたけどAttributeErrorが発生してダメだった。結局dictクラスを継承する方法しかないのかも…。残念。
エラーでなくワーニング (3)
class << h def [](k) begin self.fetch(k) rescue print "key not found\n" end end end
Rubyリファレンスマニュアルより:
fetch(key[, default])
fetch(key) {|key| ... }
key に関連づけられた値を返します。該当するキーが登録されていない時には、引数 default が与えられていればその値を、ブロックが与えられていればそのブロックを評価した値を返します。そのいずれでもなければ例外 IndexError が発生します。 (ruby 1.9 feature: IndexError の代わりに IndexError のサブクラスの KeyError が発生します。)
すばらしい!Rubyは特異メソッドが当たり前のようにできる。Pythonは間違いを犯しにくいようにわざとビルトインクラスの属性をread-onlyにしているのだろうが、継承でのカスタマイズは可能だが、ビルトインクラスのインスタンスの特異メソッドができないし、ビルトイン以外でもnewモジュールを使用しないとできない。Pythonはクラスの機構はシンプルで好きなのだが、そういうところはいまいちかも。Pythonのクラスの機構はまだ分からないことが多いのでもう少し勉強すれば何か見えてくるかも。
ところで、jijixiさんが、__orig_getitem__ = dict.__getitem__でオリジナルの__getitem__を保存しておいているのはRubyのaliasを使う常套手段から来ているのではと思った。RubyはPythonの非結合メソッドを利用するdict.__getitem__(self, key)みたいなことはできないのだろうか?Pythonにもsuperが存在するが、superにあたるモノを使用すればできそうな気もする…。やっぱりPythonとの比較のためにRubyを少し勉強すべきかな…。
エラーでなくワーニング (4)
『Pythonクックブック』を調べたが、AutoDelegatorというのが見つかった。重要なポイント以外を"..."で略す。
class AutoDelegator(object): ... def __getattr__(self, key): ... return getattr(self.delegatee, key) ... ...
呼び出し時に見つからなかったメソッドをデリゲートされるオブジェクトに委譲して検索するのがポイント。しかし、d[1] = 'a'などは、__setattr__を定義してもうまくいかなかった。結局jijixiさんのようにデリゲートされるオブジェクトのメソッドを全てコピーした方が確実だと思う。
しかし結局、Delegateを継承した自前のMyDictクラスを定義するか、Delegateのインスタンスに特異メソッドの__getitem__を結合させるしかないので、素直にdictを継承したMyDictを作成するのが一番簡単である。ちなみに、ビルトインのdictを継承した自前クラスのインスタンスにnew.instancemethodで特異メソッド__getitem__を追加してもうまくいかなかった。ビルトインクラスの挙動はかなりクセがある。理論よりとりあえず動くが実験すべき。
まとめると、例えば例外処理を変更するなど、一般的にはビルトインクラスのオブジェクトの挙動の一部を変更したければ、ビルトインクラスを継承した自前のクラスを作成するしかないと言える。さらなる研究は必要だが今のところはこれが結論である。
補足:
結局スタート時点の問題を再考すると、エラー処理のコードを例外クラスにまとめたかったのだが、自前のMyDictクラスにまとまるので例外をフックできなくても全く問題ないと思う。