読者です 読者をやめる 読者になる 読者になる

__call__ で decorator を作ったときの謎

インスタンスメソッドのデコレータを作るとき、関数の代わりに __call__ を実装したクラスを使ってみると困ったことが起きる。

関数によるデコレータでは、メソッドが呼び出されたオブジェクトが関数に渡ってくる。
しかし、__call__ によるデコレータでは、__call__ メソッドにオブジェクトが渡ってこない。

class Decorator(object):
    
    def __init__(self, func):
        self.func = func

    def __call__(self):
        # メソッドを呼び出されたオブジェクトが届かないので、仕方なく self を渡してる
        return self.func(self)

def decorate_a(func):
    return Decorator(func)

def decorate_b(func):
    def wrap(called):
        return func(called)
    return wrap

class FooMeta(type):
    
    def __new__(cls, cls_name, bases, attrs):
        for k, v in attrs.iteritems():
            if not k.startswith('_'):
                print k, v
        return type.__new__(cls, cls_name, bases, attrs)

class Foo(object):
    
    __metaclass__ = FooMeta
    
    @decorate_a
    def a(self):
        print 'do a', self
    
    @decorate_b
    def b(self):
        print 'do b', self

Foo().a()
Foo().b()


以下は実行結果

a <__main__.Decorator object at 0x109d26bd0>
b <function wrap at 0x109d2c8c0>
do a <__main__.Decorator object at 0x109d26bd0>
do b <__main__.Foo object at 0x109d26c50>


関数によるデコレータの場合、つまり属性 b を metaclass で参照すると関数オブジェクトになる。

metaclass から Decorator オブジェクトを参照できるようにしようとの試みなのだけど、うまくいかない。