スターエンジニアになりたい

異世界転生を待って早10年

Pythonのデコレーターでメタプログラミング 【第3回】【動的に装飾】 #7

どうも hrkblog です.
久しぶりの更新になってしまった....
今回はデコレーターの最終回です.
デコレータで動的にデコレートしていきます.
早速コードを見ていきます..!!

# plus_arg() is injected into Foo
def plus_arg(self, a, b):
    return a + b


# minus_arg() is injected into Foo
def minus_arg(self, a, b):
    return a - b


def change_methods(new):
    if new.__name__ != '__new__':
        return new

    def __new__(cls, *args, **kws):
        # Add attributes
        cls.plus_arg = plus_arg
        cls.minus_arg = minus_arg

        # Delete attributes
        if hasattr(cls, 'say'):
            del cls.say
        return super(cls.__class__, cls).__new__(cls, *args, **kws)

    return __new__


class Foo(object):
    @change_methods
    def __new__():
        pass

    def say(self):
        print("Hi me:", self)


def main():
    foo = Foo()
    print('plus_arg : {}\nminus_arg : {}'.format(foo.plus_arg(1, 3), foo.minus_arg(1, 3)))

    try:
        print(foo.say())
    except AttributeError as ex:
        print('method say() has already been deleted.')
        print(ex)


if __name__ == '__main__':
    main()

まずは、mainから.

foo = Foo()
    print('plus_arg : {}\nminus_arg : {}'.format(foo.plus_arg(1, 3), foo.minus_arg(1, 3)))

    try:
        print(foo.say())
    except AttributeError as ex:
        print('method say() has already been deleted.')
        print(ex)

Fooのインスタンス生成 -> fooのplus_arg(1, 3)とfooのminus_arg(1, 3)を呼び出して
結果をそれぞれ出力してみてます.
そのあとfooのsay()メソッドを呼び出してプリントしてます.
そしてsay()がなければ'method say() has already been deleted'のメッセージをプリントすることにします.
ここだけだとなんのこっちゃて感じですね.

引数を足したり引いたりする単純なplus_arg()とminus_arg()が今回Fooに付け足すメソッドです.

# plus_arg() is injected into Foo
def plus_arg(self, a, b):
    return a + b


# minus_arg() is injected into Foo
def minus_arg(self, a, b):
    return a - b

クラスFooをみてみます.

class Foo:
    @change_methods
    def __new__():
        pass

    def say(self):
        print("Hi me:", self)

Fooは__new__にデコレーターをつけてます.
そして単純な出力をするメソッドsay()があります.
このいかにもな@change_methodsをみてみます.

def change_methods(new):
    if new.__name__ != '__new__':
        return new

    def __new__(cls, *args, **kws):
        # Add attributes
        cls.plus_arg = plus_arg
        cls.minus_arg = minus_arg

        # Delete attributes
        if hasattr(cls, 'say'):
            del cls.say
        return super(cls.__class__, cls).__new__(cls, *args, **kws)

    return __new__

普通のデコレーターですね.
引数の関数の名前が__new__ではないならそのままその返すようにしてます.
__new__だった場合は新しい__new__をあげるようにします.(new __new__じゃん...)
このnew __new__はクラスにplus_argとminus_argというattributeを付け足してます.
(__new__インスタンス生成時__init__の前に呼ばれます.)
そしてクラスにsayという属性があれば削除してしまいます.

実行結果は以下です.

plus_arg : 4
minus_arg : -2
method say() has already been deleted.
'Foo' object has no attribute 'say'

意外と簡単でしたね....?
いろんな使い道がありそうですが、
メタプログラミングってコードがカオスになりがちなんですよねw
(前に参画していた苦い記憶が蘇ってきたので今日はこの辺で......w)

今度はSpring Bootの記事でも書こうかなと思っていたり.....
Javaブロックチェーンの記事とかも書きたい.....

今回3部作にして思ったのは1回目を書くと続き書かなきゃ...となるんですが、
ちょっと日が経つと重荷になってじゃっかんめんどくさくなる...

なので次はその時書きたい記事にしたいと思います....
それではまた次回にお会いしましょう~~~~~~