🍰

Python のデコレータ

2023/07/01に公開

関数デコレータ

関数デコレータは、関数を引数として受け取り関数を戻り値として返す関数のこと。関数デコレータを使用すると、ある関数に機能を付与して新しい関数に置き換えることができる。

通常、@構文と一緒に使用される。

パラメータを持たないデコレータ

デコレータ自身がパラメータを取らないケース。

def atmark_func(f):
    def wrapper(*args, **kwargs):
        f(*args, **kwargs)
        print('function is decorated')
    return wrapper

@atmark_func
def target_func(x, y):
    print(x, y)

# 上記の@構文は下記のシンタックスシュガー
# target_func = atmark_func(target_func)

target_func(10, y=20)
# 10, 20
# function is decorated

パラメータを持つデコレータ

デコレータ自身がパラメータを取るケース。
関数の置き換えが一段深く行われる。

def atmark_func(*dargs, **dkwargs):
    def decorator(f):
        def wrapper(*args, **kwargs):
            print(f'decorator parameter is {dargs}, {dkwargs}')
            f(*args, **kwargs)
            print('function is decorated')
        return wrapper
    return decorator

@atmark_func('a', b='b')
def target_func(x, y):
    print(x, y)

# 上記の@構文は下記のシンタックスシュガー
# target_func = atmark_func('a', b='b')(target_func)

target_func(10, y=20)
# decorator parameter is ('a',), {'b': 'b'}
# 10 20
# function is decorated

デコレータの複数回適用

デコレータは複数適用することも可能である。
@構文も多段とすることで、下から順に適用される。

def atmark_func1(f):
    def wrapper(*args, **kwargs):
        f(*args, **kwargs)
        print('function is decorated by atmark_func1')
    return wrapper

def atmark_func2(f):
    def wrapper(*args, **kwargs):
        f(*args, **kwargs)
        print('function is decorated by atmark_func1')
    return wrapper

@atmark_func2
@atmark_func1
def target_func(x, y):
    print(x, y)


# 上記の@構文は下記のシンタックスシュガー
# target_func = atmark_func2(atmark_func1(target_func))

target_func(10, y=20)
# 10, 20
# function is decorated by atmark_func1
# function is decorated by atmark_func2

@構文の無意味な利用

@構文は単なる関数呼び出しのシンタックスシュガーなので、どんな関数でもデコレータとして使用できてしまう。これを行っても意味はないし、対象の関数は関数ではなくなってしまうため注意。

def atmark_func1(f):
    print('function is decorated by atmark_func1')

def atmark_func2(f):
    print('function is decorated by atmark_func2')
    return 10

@atmark_func1
def target_func1(x, y):
    print(x, y)

@atmark_func2
def target_func2(x, y):
    print(x, y)

# 上記の@構文は下記のシンタックスシュガー
# target_func1 = atmark_func1(target_func)
# target_func2 = atmark_func2(target_func)

# 対象関数は別のオブジェクトに置き換えられておりもはや関数としては使用できない
print(target_func1)
print(target_func2)
# None
# 10

クラスデコレータ

クラスを修飾して置き換えるデコレータ。関数デコレータと考え方は同じ。

def atmark_func(cls):
    # 修飾するための実装 ...
    return cls

@atmark_func
class Foo():
    ...

# 上記の@構文は下記のシンタックスシュガー
# Foo = atmark_func(Foo)

Discussion