Python : デコレータは既存の関数に機能を追加する関数

TwitterFacebookHatena
  • 公開:2021-9-14
  • 更新:2023-10-26
  • 文章量:1842
  • Python

TL;DR

デコレータを一言で言うと関数の装飾(上書き)である。ラップする関数への呼び出しの前後で追加コードを実行することができる。これによって、入力の引数や戻り値にアクセスして値を変更したり例外を送出したりできる。この機能は、ログイン状態による制限・関数登録・引数のチェックなどを行うのに役立つ。

関数デコレータの実体は、引数に関数を一つ受け取り「別の関数を返す関数」である。

ソースコードを書き換えずに既存の関数やクラスに変更を加えたいときはデコレータを使う。デコレータを使うと、既存のクラスや関数に対してコードの変更なしに処理を追加することができるようになる。デコレータは、入力として関数を一つとり、別の関数を返す関数である。デコレータを使うことによって、コードの重複を減らすことができる。

デコレータのポイント

デコレータの名前は「装飾する」という意味を持つ decorate からきている。

  • 既存のクラスや関数に機能を追加する関数
  • 関数内関数
  • 引数としての関数
  • 関数を変数で扱うことにより、関数の引数や戻り値として扱うことができる
  • 変数のように参照させることができる
  • クラスをインスタンス化する必要がない
  • 関数やクラスの前に @ で始まる文字列を記述する

デコレータ

Python では関数内で関数を定義できる。外側の関数は内側の関数を呼び出して使う。

デコレータは、ある関数に対し、指定した関数を呼び出す。以下は、関数 b に対して 関数 a を追加している。

def a(x):
    def new_a():
        print('before')
        x() # 元の関数を実行する
        print('after')

    return new_a

@a
def b():
    print('middle')

b()

# before
# middle
# after

アスタリスクを使って、任意の数の引数(可変長引数)を指定できる。どんな引数を受け取る関数に対しても使える。

def dec(f):
    def inner(*args, **kwargs):
        print('before')
        f(*args, **kwargs)
        print('after')
    return inner

@dec
def hoge(a):
    print(a)

hoge(1)
# before
# 1
#after

hoge('text')
# before
# text
# after

hoge(False)
# before
# False
# after

入門 Python3 から、ソースコードを引用させていただく。次のコードは、複数の関数をデコレータを add_ints() にデコレートしている例。デコレータの順番を返ると結果も変更される。

# 関数1
def document_it(func):
    def new_function(*args, **kwargs):
        print('関数名', func.__name__)
        print('args', args)
        print('kwargs', kwargs)
        result = func(*args, **kwargs)
        print('result', result)
        return result
    return new_function

# 関数2
def square_it(func):
    def new_function(*args, **kwargs):
        result = func(*args, **kwargs)
        return result * result
    return new_function

# デコレート
@document_it
@square_it
def add_ints(a, b):
    return a + b

print(add_ints(3, 5))
# 関数名 new_function
# args (3, 5)
# kwargs {}
# result 64
# 64

Python : デコレータは既存の関数に機能を追加する関数