【Python】デコレータ(decorator)とは何か?
今日は、Pythonのとっても便利な機能「デコレータ」について、わかりやすくご紹介します。
デコレータって聞くと難しそうに感じますが、実はケーキにトッピングをするみたいなもので、とってもシンプルな考え方なんです!
デコレータって何?
デコレータとは、簡単に言うと「関数を包み込んで、新しい機能を追加するための仕組み」。
名前の通り、何かを「装飾する(デコレーションする)」ためのものなんです。
例えば、お誕生日ケーキを想像してみてください。
普通のケーキにイチゴやチョコレートをトッピングすると、見た目も味も変わりますよね?
それと同じで、デコレータは既存の関数に新しい機能を「トッピング」するんです。
デコレータの基本的な使い方
Pythonでデコレータを使うときは、「@」記号を使います。これは「アットマーク」と読みます。
def my_decorator(func):
def wrapper():
print("デコレータで追加した処理: 関数実行前") # デコレータで追加した処理
func() # 元の関数を実行
print("デコレータで追加した処理: 関数実行後") # デコレータで追加した処理
return wrapper
@my_decorator
def say_hello():
print("こんにちは!")
# 関数を実行
say_hello()
# 出力:
# デコレータで追加した処理: 関数実行前
# こんにちは!
# デコレータで追加した処理: 関数実行後
このコードでは、say_hello
関数の前後に、新しい処理(メッセージを表示する)を追加しています。@my_decorator
という記号で、say_hello
関数をデコレーター(my_decorator
)で包んでいるんです。
デコレータのしくみをもっと詳しく
デコレータの働きをもうちょっと詳しく見てみましょう。
実は、@my_decorator
を使うことは、以下のコードと同じ意味になります。
def say_hello():
print("こんにちは!")
# デコレータを使わない場合は、こう書きます
decorated_hello = my_decorator(say_hello)
decorated_hello()
つまり、デコレータは関数を別の関数で包んで、新しい関数を作り出しているんです。
おしゃれな包装紙でプレゼントを包むようなイメージですね!
デコレータで引数を扱う方法
実際のプログラムでは、関数に引数がある場合が多いですよね。
デコレータでそんな関数を扱うには、以下のようにします。
def my_decorator_with_args(func):
def wrapper(*args, **kwargs):
print(f"関数が実行されました。引数: {args}, キーワード引数: {kwargs}") # 引数の表示
result = func(*args, **kwargs) # 元の関数を実行して結果を取得
print(f"関数の実行が終わりました。結果: {result}") # 結果の表示
return result # 結果を返す
return wrapper
@my_decorator_with_args
def add_numbers(a, b):
return a + b
# 関数を実行
result = add_numbers(3, 5)
print(f"最終結果: {result}")
# 出力:
# 関数が実行されました。引数: (3, 5), キーワード引数: {}
# 関数の実行が終わりました。結果: 8
# 最終結果: 8
このコードでは、*args
と**kwargs
という特殊な記法を使って、どんな引数でも受け取れるようにしています。
まるで何でも入れられる魔法のポケットのようですね!
ここでは**kwargs
は必須ではありません。
**kwargs
は、キーワード引数(名前付き引数)を任意の数だけ受け取るための構文です。
例の中では実際にキーワード引数を使っていないため、省略することもできます。
ただし、実際のプログラミングでは、将来的にキーワード引数が必要になる可能性も考慮して、*args
と**kwargs
を両方含めておくことが多いです。
これは「念のため」というか、より汎用的なデコレータにするための習慣のようなもの。
デコレータを書くときは、ラップする関数がどんな引数を取るか分からない場合が多いので、*args
, **kwargs
の両方を含めておくと安全です。
でも、確実にキーワード引数を使わないとわかっている場合は、**kwargs
を省略しても問題ありません!
デコレータの実用例
デコレータは実際のプログラミングでどう使われるのでしょうか?いくつか例を見てみましょう。
例1: 実行時間を計測するデコレータ
import time
def measure_time(func):
def wrapper(*args, **kwargs):
start_time = time.time() # 開始時間を記録
result = func(*args, **kwargs) # 関数を実行
end_time = time.time() # 終了時間を記録
print(f"関数 {func.__name__} の実行時間: {end_time - start_time:.4f}秒") # 実行時間を表示
return result
return wrapper
@measure_time
def slow_function():
time.sleep(1) # 1秒間停止
print("処理が完了しました")
# 関数を実行
slow_function()
# 出力:
# 処理が完了しました
# 関数 slow_function の実行時間: 1.0013秒
このデコレータは、関数の実行にかかる時間を測定して表示します。
特に時間がかかる処理のパフォーマンスを確認するのに便利です。
例2: ログを記録するデコレータ
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"関数 {func.__name__} が呼び出されました") # 関数の呼び出しを記録
result = func(*args, **kwargs) # 関数を実行
print(f"関数 {func.__name__} が終了しました") # 関数の終了を記録
return result
return wrapper
@log_function_call
def greet(name):
print(f"こんにちは、{name}さん!")
return f"{name}さんへの挨拶が完了しました"
# 関数を実行
result = greet("太郎")
print(result)
# 出力:
# 関数 greet が呼び出されました
# こんにちは、太郎さん!
# 関数 greet が終了しました
# 太郎さんへの挨拶が完了しました
このデコレータは、関数の呼び出しと終了のタイミングをログとして記録します。
デバッグやプログラムの流れを追跡するのに役立ちます。
デコレータのまとめ
デコレータの特徴を表でまとめてみましょう。
特徴 | 説明 |
---|---|
構文 |
@デコレータ名 を関数の前に記述 |
主な用途 | 既存の関数に新しい機能を追加する |
メリット | コードの再利用性が高まる、機能の追加が簡単 |
実用例 | 実行時間の計測、ログの記録、認証、キャッシュなど |
まとめ
Pythonのデコレータは、既存の関数に新しい機能を追加するための強力な仕組みです。
ケーキにトッピングを追加するように、簡単に機能を拡張することが可能。
デコレータを使いこなせるようになると、コードがよりスッキリして、読みやすくなります。
また、同じコードを繰り返し書く必要がなくなるので、プログラマーの大敵である「繰り返し(DRY原則:Don't Repeat Yourself)」も避けられます。
ぜひ、自分のプログラムでデコレータを試してみてください。
Discussion