xmlrpclibの__getattr__

別の探し物をしている時に見つけて、何でそうなるのか判らなかったんで調べてみた。

class _Method:
    def __init__(self, send, name):
        print '_Method.__init__:', name
        self.__send = send
        self.__name = name

    def __getattr__(self, name):
        print '_Method.__getattr__:', name
        return _Method(self.__send, "%s.%s" % (self.__name, name))

    def __call__(self, *args):
        print '_Method.__call__:', self.__name, args
        return self.__send(self.__name, args)

class Demo:
    def __getattr__(self, name):
        print 'Demo.__getattr__:', name
        return _Method(self.__request, name)

    def __request(self, *args):
        print 'Demo.__request:', args

# テスト
a = Demo()
a.kma.to.hoge('kma', 2, 'hoge')

# 結果
Demo.__getattr__: kma
_Method.__init__: kma
_Method.__getattr__: to
_Method.__init__: kma.to
_Method.__getattr__: hoge
_Method.__init__: kma.to.hoge
_Method.__call__: kma.to.hoge ('kma', 2, 'hoge')
Demo.__request: ('kma.to.hoge', ('kma', 2, 'hoge'))

あー、そういう事ね。
a.kma.to.hoge('kma', 2, 'hoge') を一時オブジェクトを使用して判り易くしてみた。

t1 = a.kma           # "Demo.__getattr__: kma"
                     # Demo.__getattr__ で _Method(self.__request, 'kma') を生成
                     # "_Method.__init__: kma"
                     # t1 は _Method のインスタンス
                     # t1.__send = Demo.__request
                     # t1.__name = 'kma'

t2 = t1.to           # "_Method.__getattr__: to"
                     # _Method.__getattr__ で _Method(self.__request, 'kma.to') を生成
                     # "_Method.__init__: kma.to"
                     # t2 は _Method のインスタンス
                     # t2.__send = Demo.__request
                     # t2.__name = 'kma.to'

t3 = t2.hoge         # "_Method.__getattr__: hoge"
                     # _Method.__getattr__ で _Method(self.__request, 'kma.to.hoge') を生成
                     # "_Method.__init__: kma.to.hoge"
                     # t3 は _Method のインスタンス
                     # t3.__send = Demo.__request
                     # t3.__name = 'kma.to.hoge'

t3('kma', 2, 'hoge') # インスタンスが関数として ``呼ばれた'' ので __call__ メソッドが呼び出される。
                     # "_Method.__call__: kma.to.hoge ('kma', 2, 'hoge')"
                     # _Method.__call__ で Demo.__request('kma.to.hoge', ('kma', 2, 'hoge'))
                     # "Demo.__request: ('kma.to.hoge', ('kma', 2, 'hoge'))"