ひどく自分勝手なデコレーターの作り方

はじめに

デコレーターを作成する時引数を取るのか引数を取らないのか混同しやすい。 どちらであっても許容できるデコレーターがあっても良いのかもしれないというような実験

引数なしのデコレーター

デコレーターを作る時まず以下のような関数に適用する引数無しのデコレーターを考える。

def attach(f):
    def _attach(*args, **kwargs):
        print("attached: {}, {}".format(f, args))
        return f(*args, **kwargs)
    return _attach

使い方は以下のよう。

@attach
def f(x):
    return x * x

f(10)

# attached: <function f at 0x1052104d0>, (10,)

引数付きのデコレーター

引数を追加するには関数のネストをもう一段階追加してあげれば良い。クラスにして __init__()__call__() を実装しても良い。

def attach_with_args(message):
    def _attach_with_args(f):
        def _attach(*args, **kwargs):
            print(message.format(f, args))
            return f(*args, **kwargs)
        return _attach
    return _attach_with_args

使い方は以下。

@attach_with_args(message="new attached: {}, {}")
def f(x):
    return x * x

f(100)

# new attached: <function f at 0x105210680>, (100,)

両方対応する方法

引数をデフォルトの引数を上書きする機能というように考えると以下の様に定義すれば良い。 元となる対象の関数fが存在しなかったらきっとデフォルトの引数を上書きたかったということにする。 (型のことは忘れよう。。)

def attach_with_args_with_kindness(f=None, message="attached: {}, {}"):
    """@attach() and @attach also ok"""
    def call(f):
        def _attach(*args, **kwargs):
            print(message.format(f, args))
            return f(*args, **kwargs)
        return _attach

    if f is not None:
        return call(f)
    else:
        return call

使い方はこう。

@attach_with_args_with_kindness
def f(x):
    return x * x

f(1000)

@attach_with_args_with_kindness(message="hai: {}, {}")
def f(x):
    return x * x

f(1000)

# attached: <function f at 0x105210560>, (1000,)
# hai: <function f at 0x105210680>, (1000,)

gist

kindness_decorator.py · GitHub