Pythonデコレータマスター:エキスパート技法で関数を拡張
Pythonデコレータ
デコレータで関数を再利用可能なロジックでラップし、コードをよりモジュール化、可読、保守しやすくします。このガイドではデコレータの構築、説明、実践的シナリオへの適用を行います。
動画:Python @デコレータ
Pythonにおけるデコレータ
Pythonのデコレータは、定義時に関数やクラスに振る舞いを追加する強力なメタプログラミングツールです。Pythonではすべてがオブジェクト—関数やクラスも含む—ため、デコレータは1つの呼び出し可能オブジェクトを受け取り、別の呼び出し可能オブジェクトでラップし、新しい呼び出し可能オブジェクトを返すことで動作します。
デコレータ学習の前提知識
Pythonでは関数がファーストクラスオブジェクトであることに慣れておく必要があります。名前は単にオブジェクトへの参照であり、同じ関数を複数の名前で参照できます。
def first(msg):
print(msg)
first("Hello")
second = first
second("Hello")
出力
Hello Hello
関数は引数として渡せ、他の関数から返せ、他の関数内でネスト可能。これら—高階関数、ネスト関数、クロージャ—がデコレータの構成要素です。
def inc(x):
return x + 1
def dec(x):
return x - 1
def operate(func, x):
result = func(x)
return result
使用例:
>>> operate(inc, 3)
4
>>> operate(dec, 3)
2
def is_called():
def is_returned():
print("Hello")
return is_returned
new = is_called()
new()
出力
Hello
クロージャ理解が鍵:ネスト関数が外側スコープへのアクセスを保持し、これがデコレータがメタデータ格納や振る舞い変更に利用する仕組みです。
デコレータに戻る
Pythonでは、デコレータは単に関数(または他の呼び出し可能)を受け取り、新しい呼び出し可能を返す呼び出し可能オブジェクトです。返された関数は通常、元関数をラップし、呼び出し前後に機能追加・変更を行います。
def make_pretty(func):
def inner():
print("I got decorated")
func()
return inner
def ordinary():
print("I am ordinary")
使用例:
>>> ordinary()
I am ordinary
>>> pretty = make_pretty(ordinary)
>>> pretty()
I got decorated
I am ordinary
デコレータパターンは元関数のセマンティクスを保ちつつ拡張します。手動再代入を避けるため、Pythonは@シンボルでシンタックスシュガーを提供:
@make_pretty
def ordinary():
print("I am ordinary")
これは以下と等価:
def ordinary():
print("I am ordinary")
ordinary = make_pretty(ordinary)
パラメータ付き関数のデコレート
多くのデコレータは任意の引数を扱う必要があります。*argsと**kwargs構文で任意の位置・キーワード引数を捕捉し、そのまま転送できます。
def works_for_all(func):
def inner(*args, **kwargs):
print("I can decorate any function")
return func(*args, **kwargs)
return inner
エッジケース処理もデコレータの一般的ユースケース。例えばゼロ除算ガード:
def smart_divide(func):
def inner(a, b):
print("I am going to divide", a, "and", b)
if b == 0:
print("Whoops! cannot divide")
return
return func(a, b)
return inner
@smart_divide
def divide(a, b):
print(a/b)
使用例:
>>> divide(2, 5)
I am going to divide 2 and 5
0.4
>>> divide(2, 0)
I am going to divide 2 and 0
Whoops! cannot divide
Pythonでのデコレータチェーン
デコレータはスタック可能。適用順序が重要で、各デコレータは下のデコレータが返した関数を受け取ります。
def star(func):
def inner(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return inner
def percent(func):
def inner(*args, **kwargs):
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return inner
@star
@percent
def printer(msg):
print(msg)
結果:
****************************** %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Hello %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ******************************
順序を逆にすると周囲パターンが入れ替わり、デコレータスタックが振る舞いにどう影響するか示しています。
Python
- Python データ型マスター: 実践ガイド
- Python 演算子完全マスター: 包括的ガイド
- Python pass文完全マスター: 実践ガイド
- Python 関数引数完全マスター: 位置引数・キーワード引数・デフォルト引数
- Python ラムダ関数: 無名関数の実践ガイド
- Pythonラムダ関数をマスターする:実例とベストプラクティス
- Python abs()関数:数値と複素数の絶対値を計算
- Python round()関数を実例で徹底解説
- Python range()関数をマスター:基礎から応用まで
- Python map()関数をマスター:構文、例、ベストプラクティス
- Python yieldをマスター:ジェネレータvs return – 実践ガイド