Pythonデコレータ完全攻略 – 基本から実践応用まで徹底解説
デコレータとは?3分で理解する基本概念
**デコレータ(Decorator)**は、既存の関数やクラスに新しい機能を追加するPythonの仕組みです。元のコードを変更せずに、ログ出力や実行時間計測などの機能を簡単に追加できます。
デコレータの基本構文
# デコレータなし
def hello():
print("Hello, World!")
# デコレータあり
@my_decorator
def hello():
print("Hello, World!")
Code language: PHP (php)
最も基本的なデコレータの作り方
シンプルなデコレータ
def my_decorator(func):
def wrapper():
print("処理開始")
func()
print("処理終了")
return wrapper
@my_decorator
def greet():
print("こんにちは!")
greet() # 処理開始 → こんにちは! → 処理終了
Code language: PHP (php)
引数を持つ関数用のデコレータ
def logger(func):
def wrapper(*args, **kwargs):
print(f"関数 {func.__name__} を実行")
result = func(*args, **kwargs)
print(f"結果: {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(3, 5) # 関数 add を実行 → 結果: 8
Code language: PHP (php)
実用的なデコレータ集
1. 実行時間計測デコレータ
import time
from functools import wraps
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__}: {time.time() - start:.4f}秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "完了"
Code language: JavaScript (javascript)
2. リトライ機能デコレータ
def retry(max_attempts=3):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise e
print(f"リトライ {attempt + 1}/{max_attempts}")
return wrapper
return decorator
@retry(max_attempts=3)
def unstable_api_call():
import random
if random.random() < 0.7:
raise ConnectionError("API接続失敗")
return "成功"
Code language: PHP (php)
3. キャッシュデコレータ
def cache(func):
cached_results = {}
@wraps(func)
def wrapper(*args):
if args in cached_results:
print(f"キャッシュから取得: {args}")
return cached_results[args]
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
Code language: PHP (php)
パラメータ付きデコレータ
引数を受け取るデコレータ
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def say_hello():
print("Hello!")
say_hello() # Hello! が3回出力される
Code language: PHP (php)
条件付き実行デコレータ
def run_if(condition):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if condition():
return func(*args, **kwargs)
print(f"{func.__name__} はスキップされました")
return wrapper
return decorator
@run_if(lambda: datetime.now().hour < 18)
def work_hours_only():
print("業務時間内の処理")
Code language: PHP (php)
クラス用デコレータ
クラスメソッドのデコレータ
class APIClient:
@timer
def fetch_data(self, url):
import requests
response = requests.get(url)
return response.json()
@retry(max_attempts=2)
def post_data(self, url, data):
import requests
return requests.post(url, json=data)
Code language: CSS (css)
プロパティデコレータ
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("半径は正の値である必要があります")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
Code language: HTML, XML (xml)
高度なデコレータパターン
複数デコレータの組み合わせ
@timer
@retry(max_attempts=2)
@cache
def complex_calculation(n):
import time
time.sleep(0.1) # 重い処理をシミュレート
return n ** 2
# 実行順序: cache → retry → timer → complex_calculation
Code language: PHP (php)
クラスベースのデコレータ
class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} が {self.count} 回目の呼び出し")
return self.func(*args, **kwargs)
@CountCalls
def greet(name):
return f"Hello, {name}!"
デコレータファクトリーパターン
def validate_types(**expected_types):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for arg_name, expected_type in expected_types.items():
if arg_name in kwargs:
if not isinstance(kwargs[arg_name], expected_type):
raise TypeError(f"{arg_name} は {expected_type} である必要があります")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(name=str, age=int)
def create_user(name, age):
return {"name": name, "age": age}
Code language: JavaScript (javascript)
実務でよく使うデコレータ活用例
1. API認証デコレータ
def require_auth(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not hasattr(wrapper, 'user') or not wrapper.user:
raise PermissionError("認証が必要です")
return func(*args, **kwargs)
return wrapper
@require_auth
def get_user_data(user_id):
return f"ユーザー {user_id} のデータ"
Code language: JavaScript (javascript)
2. ログ出力デコレータ
import logging
from datetime import datetime
def log_calls(level=logging.INFO):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.log(level, f"{datetime.now()}: {func.__name__} 開始")
try:
result = func(*args, **kwargs)
logging.log(level, f"{datetime.now()}: {func.__name__} 成功")
return result
except Exception as e:
logging.error(f"{datetime.now()}: {func.__name__} エラー: {e}")
raise
return wrapper
return decorator
@log_calls(level=logging.DEBUG)
def process_data(data):
return [x * 2 for x in data]
Code language: JavaScript (javascript)
3. レート制限デコレータ
import time
from collections import defaultdict
def rate_limit(calls_per_second=1):
call_times = defaultdict(list)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
func_calls = call_times[func.__name__]
# 1秒以内の呼び出しをフィルタ
recent_calls = [t for t in func_calls if now - t < 1]
if len(recent_calls) >= calls_per_second:
raise Exception("レート制限に達しました")
call_times[func.__name__] = recent_calls + [now]
return func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(calls_per_second=2)
def api_call():
return "API レスポンス"
Code language: PHP (php)
よくある間違いと対処法
1. functools.wrapsを忘れる
# ❌ 関数のメタデータが失われる
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# ✅ メタデータを保持
from functools import wraps
def good_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
Code language: PHP (php)
2. クラスメソッドでのself引数の扱い
def method_decorator(func):
@wraps(func)
def wrapper(self, *args, **kwargs): # selfを明示的に受け取る
print(f"{self.__class__.__name__}.{func.__name__} 実行")
return func(self, *args, **kwargs)
return wrapper
class MyClass:
@method_decorator
def my_method(self, value):
return value * 2
Code language: CSS (css)
パフォーマンスへの影響
軽量なデコレータの作り方
# ❌ 重いデコレータ
def heavy_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 毎回重い処理
import time
time.sleep(0.01)
return func(*args, **kwargs)
return wrapper
# ✅ 軽量なデコレータ
def light_decorator(func):
# 初期化時に1回だけ実行
func._call_count = 0
@wraps(func)
def wrapper(*args, **kwargs):
func._call_count += 1
return func(*args, **kwargs)
return wrapper
Code language: PHP (php)
デバッグとテスト
デコレータのテスト方法
def test_timer_decorator():
@timer
def sample_func():
import time
time.sleep(0.1)
return "done"
result = sample_func()
assert result == "done"
# タイマー出力を確認
def test_retry_decorator():
attempt_count = 0
@retry(max_attempts=3)
def failing_func():
nonlocal attempt_count
attempt_count += 1
if attempt_count < 3:
raise Exception("失敗")
return "成功"
result = failing_func()
assert result == "成功"
assert attempt_count == 3
Code language: PHP (php)
まとめ:デコレータ活用のベストプラクティス
効果的な使用場面
- 横断的関心事(ログ、認証、キャッシュ)
- コードの重複削減
- 機能の後付け追加
注意点
- 過度な使用は可読性を損なう
- デバッグが困難になる場合がある
- パフォーマンスへの影響を考慮
推奨事項
functools.wrapsを必ず使用- シンプルで理解しやすいデコレータを心がける
- 適切なテストを作成する
デコレータをマスターすることで、コードの再利用性と保守性が大幅に向上します。まずは基本的なタイマーやログ出力から始めて、徐々に複雑なパターンに挑戦していきましょう。
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座


