この記事では、Pythonのデコレーターの使い方を解説します。
デコレーターとは、関数をラップするための機能です。
関数にデコレーターを付けると、その関数を呼び出した際にデコレーター定義関数に呼び出した関数を引数として渡し、デコレーター定義関数を実行することができます。
それでは、デコレーターの使い方を見ていきましょう!
デコレーターの使い方
デコレーターは、以下のように使用します。
-
STEP1デコレーター定義関数の定義デコレーター定義関数は、以下のように関数をネストして定義します。内側の関数の
wrapperという名前には構文的な意味はありませんがよく使用されます。def デコレーター名(func): def wrapper(*args, **kw): # 処理 return wrapperfunc引数 デコレーターを付与した関数の参照が渡される *args引数, **kw引数 デコレーターを付与した関数の引数が渡される -
STEP2修飾する作成したデコレータは、以下のように関数に修飾します。
@デコレーター名 def 何らかの関数(): # 処理
試しに、簡単なデコレーターを定義して使ってみましょう。以下のデコレーターは、関数を実行する前後に文字を出力するだけです。
# デコレーター定義関数 def decorator(func): def wrapper(*args, **kw): print('デコレーター 開始') # 受け取った関数の参照に引数を渡して実行 func(*args, **kw) print('デコレーター 終了') return wrapper # func関数にデコレータを付与 @decorator def func(): print('func関数 実行') # func関数実行 func()
実行結果
デコレーター 開始 func関数 実行 デコレーター 終了
デコレーターと同等の呼び出し
デコレータとして付与しなくても呼び出し方で同じ処理を実行できます。
簡単な関数を2つ作成し、1つにはデコレーターを付けました。この2つの関数が同じ結果になるように呼び出してみます。
def deco(func): def wrapper(*args, **kw): print('deco - start') func(*args, **kw) print('deco - end') return wrapper @deco def func1(): print('func1') def func2(): print('func2') # デコレーター有り func1() # デコレーター無し deco(func2)()
実行結果
>> func1の実行結果 deco - start func1 deco - end >> func2の実行結果 deco - start func2 deco - end
まぎらわしく見えますが、deco関数を実行するとwrapper関数の参照を返しているだけです。そして、返されたwrapper関数の参照を()で実行しています。
しかし、この呼び出し方は直感的にわかりづらいし、デコレーターを削除する際には呼び出し元を全て書き換える必要があるのでめんどい。
docstringが参照できない?
デコレーターを付与すると、付与された関数のドキュメンテーション文字列が参照不能になってしまう問題があります。
# デコレーター定義関数 def decorator(func): def wrapper(*args, **kw): pass return wrapper # ドキュメンテーション文字列が定義された関数にデコレーターを付与 @decorator def func(): """ドキュメンテーション文字列""" pass # func関数のドキュメンテーション文字列を参照 print(func.__doc__)
実行結果
None
そんな時は、functoolsモジュールのwraps()をwrapper関数に付与します。
from functools import wraps def decorator(func): @wraps(func) # wrapperに付与 def wrapper(*args, **kw): pass return wrapper @decorator def func(): """ドキュメンテーション文字列""" pass print(func.__doc__)
実行結果
ドキュメンテーション文字列
これでラップした関数のドキュメンテーション文字列を参照することができます。基本的にwraps()はあった方が良いです。
引数を使用するデコレーター
引数が定義されている関数にデコレーターを付与させることもできます。いろいろな引数を定義した関数に、デコレーターを付けて動作を確認してみます。
def decorator(func): def wrapper(*args, **kw): func(*args, **kw) return wrapper # 通常の引数と可変長引数を取る関数 @decorator def test(text, *args, **kw): print(text) print(args) print(kw) test('test関数です', 1, 2, 3, one=1, two=2, three=3)
実行結果
test関数です (1, 2, 3) {'one': 1, 'two': 2, 'three': 3}
func(*args, **kw)によって、全て?の引数を正常に引き渡すことができます。
戻り値を返すデコレーター
戻り値を受け取りたい場合は、wrapper関数でreturnすれば良い。
def decorator(func): def wrapper(*args, **kw): print(func.__name__ + '関数 実行') return func(*args, **kw) # 戻り値を返す return wrapper @decorator def add(x, y): return x + y print(add(1, 2))
実行結果
add関数 実行 3
デコレーター自体に引数を定義する
デコレーターに引数を定義するには、デコレーター定義関数にさらに関数を重ねます。
def デコレーター名(仮引数): def _デコレータ名(func): def wrapper(*args, **kw): # なんらかの処理 return wrapper return _デコレータ名
引数は、関数に付与する際に渡します。
@デコレーター名(実引数) def 関数(): # なんらかの処理
試しに、デコレーターから引数を受け取り出力してみます。
def deco(num): def _deco(func): def wrapper(*args, **kw): print(num) # 受け取った引数を出力 return wrapper return _deco @deco(1982) def func(): pass func()
実行結果
1982
見た目がゴチャつくので難しく感じますが、やっていることは単純です。
デコレーターを複数付ける
デコレーターは、1つの関数に複数つけることも可能です。
def deco1(func): def wrapper(*args, **kw): print('deco1 start') func(*args, *kw) print('deco1 end') return wrapper def deco2(func): def wrapper(*args, **kw): print('deco2 start') func(*args, **kw) print('deco2 end') return wrapper @deco1 @deco2 def test(): print('test関数') test()
実行結果
deco1 start deco2 start test関数 deco2 end deco1 end
同等の呼び出し
覚える必要はないですが一応参考までに...。
先ほどの複数のデコレーターを付けた関数と同等の呼び出し方法を見てみます。
deco1(deco2(test))()
うん、めちゃくちゃわかりずらい❗️
デコレーターを使うことでシンプルに表現できているのがわかります。
まとめ
この記事では、Pythonのデコレーターについて解説しました。
デコレーターを使うことで簡単に関数をラップすることができます。あまり使う機会は多くないかもしれませんが、便利な機能なので頭の片隅に入れておきましょう!
それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ
- PEP 318 -- Decorators for Functions and Methods | Python.org


