Ruby,Pythonで動的プロキシ

メタプログラミングRubyで動的プロキシというものが紹介されています

どのメソッドにも当てはまらないメッセージを他のオブジェクトに委譲する、というものです

下記はメタプログラミングRubyで紹介されているサンプルコードとほぼ同じものです

class MyDynamicProxy
  def initialize(target)
    @target = target
  end

  def method_missing(name, *args, &block)
    "result : #{@target.send(name, *args, &block)}"
  end
end

obj = MyDynamicProxy.new("a string")
print obj.upcase
# => result : A STRING


rubyではゴーストメソッド(method_missing)と動的ディスパッチ(sendによるメソッド呼び出し)を使って動的プロキシを実装しています

pythonで動的プロキシ


pythonではmethod_missingに近いものとして、__getattr__という特殊なメソッドがあります
メソッド呼び出しで該当するメソッドがオブジェクトツリーに見つからなかった場合に呼ばれます

class C:
    def __getattr__(self, attrname):
        return attrname.upper()

obj = C()
print obj.upper
# => UPPER


動的ディスパッチについては、getattrを使って同等のことができます

method_to_call = 'upper'

obj = "abc"

getattr(obj, method_to_call)()
# => "ABC"

参考)
RubyPythonで動的ディスパッチ/動的メソッド定義
http://d.hatena.ne.jp/yuheiomori0718/20120116/1326715412


以上の__getattr__、getattrを使って、rubyのサンプルと同等のpythonコードは以下のように書けます

class MyDynamicProxy:

    def __init__(self, target):
        self.target = target

    def __getattr__(self, attrname):
        print("result : ")
        return getattr(self.target, attrname)


obj = MyDynamicProxy("a string")
print(obj.upper())
# => result:
# => A STRING

ちょっとだけ異なる点として、rubyではmethod_missing内でtargetの処理まで呼び出しているのに対し、
pythonの__getattr__はtargetのメソッドオブジェクトを取得して返すだけで、処理自体は呼び出し元のスコープで行なっています。

__getattr__には引数は渡されませんので、引数の必要なメソッドの場合、内部で処理を行うことはできません


参考)

メタプログラミングRuby

メタプログラミングRuby

初めてのPython 第3版

初めてのPython 第3版