Pythonデコレータ
デコレータ(decorator)はPythonにおける高度な機能の一つで、以下のために使用されます元の関数コードを変更せずに、関数やクラスの機能を動的に拡張します。
本質的に、デコレータは関数です。関数を引数として受け取り、新しい関数(通常は元の関数を拡張したバージョン)を返します。
デコレータは@decorator_name構文を使用して関数やメソッド定義の前に適用します。
Pythonには組み込みデコレータもいくつか用意されています。例:@staticmethodと@classmethod。
よくある適用シナリオ:
- ログ記録:関数呼び出し情報、引数、戻り値を記録します
- パフォーマンス統計:関数の実行時間を計測します
- 権限制御:関数のアクセス権限を制限します
- キャッシュ:関数の結果をキャッシュし、パフォーマンスを向上させます
基本構文
デコレータの核心的な考え方は:ある関数で別の関数を「ラップ」することです。
構文
def decorator_function(original_function):
def wrapper(*args, **kwargs):
# 呼び出し前
print("実行前")
result = original_function(*args, **kwargs)
# 呼び出し後
print("実行後")
return result
return wrapper
@decorator_function
def target_function():
print("元の関数を実行")
def wrapper(*args, **kwargs):
# 呼び出し前
print("実行前")
result = original_function(*args, **kwargs)
# 呼び出し後
print("実行後")
return result
return wrapper
@decorator_function
def target_function():
print("元の関数を実行")
解説:
decorator_function:デコレータ関数(元の関数を受け取る)wrapper:ラップ関数(実際に実行される)@decorator_function:関数の置き換えに相当
同等の書き方:
target_function = decorator_function(target_function)
👉 呼び出しtarget_function()時、実際に実行されるのはwrapper()
デコレータの使用
デコレータは@シンタックスシュガーを使用して関数定義の前に適用します:
@time_logger
def target_function():
pass
次と同等:
def target_function():
pass
target_function = time_logger(target_function)
このメカニズムにより、元の関数を変更せずに、ログや権限などの機能を統一的に追加できます。
例:ログ出力
def my_decorator(func):
def wrapper():
print("関数実行前")
func()
print("関数実行後")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
def wrapper():
print("関数実行前")
func()
print("関数実行後")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
出力:
函数执行前 Hello! 函数执行后
my_decorator受け取るsay_hello@my_decorator元の関数を置き換える
引数を持つデコレータ
元の関数に引数がある場合、wrapperを使用する必要があります*args, **kwargs:
例
def my_decorator(func):
def wrapper(*args, **kwargs):
print("実行前")
func(*args, **kwargs)
print("実行後")
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
def wrapper(*args, **kwargs):
print("実行前")
func(*args, **kwargs)
print("実行後")
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
出力:
执行前 Hello, Alice! 执行后
説明:を使用すると*args, **kwargs任意の引数の関数に対応できます。
引数を持つデコレータ(上級)
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
説明:これは「デコレータファクトリー」で、外側の関数が引数を受け取ります。
Hello! Hello! Hello!
クラスデコレータ
関数だけでなく、デコレータはクラスにも適用できます。
クラスデコレータはクラスを受け取り、変更後またはラップしたクラスを返します。
- クラスメソッドの拡張
- インスタンス化プロセスの制御
- シングルトン、ログなどの機能を実装
関数形式のクラスデコレータ
例
def log_class(cls):
class Wrapper:
def __init__(self, *args, **kwargs):
self.wrapped = cls(*args, **kwargs)
def __getattr__(self, name):
return getattr(self.wrapped, name)
def display(self):
print("呼び出し前")
self.wrapped.display()
print("呼び出し後")
return Wrapper
@log_class
class MyClass:
def display(self):
print("元のメソッド")
obj = MyClass()
obj.display()
class Wrapper:
def __init__(self, *args, **kwargs):
self.wrapped = cls(*args, **kwargs)
def __getattr__(self, name):
return getattr(self.wrapped, name)
def display(self):
print("呼び出し前")
self.wrapped.display()
print("呼び出し後")
return Wrapper
@log_class
class MyClass:
def display(self):
print("元のメソッド")
obj = MyClass()
obj.display()
呼び出し前 元のメソッド 呼び出し後
クラス形式のクラスデコレータ
例:シングルトンパターン
class SingletonDecorator:
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@SingletonDecorator
class Database:
def __init__(self):
print("初期化")
db1 = Database()
db2 = Database()
print(db1 is db2)
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@SingletonDecorator
class Database:
def __init__(self):
print("初期化")
db1 = Database()
db2 = Database()
print(db1 is db2)
初始化 True
組み込みデコレータ
よく使われる組み込みデコレータ:
- @staticmethod:静的メソッドを定義
- @classmethod:クラスメソッドを定義
- @property:メソッドをプロパティに変換
例
class MyClass:
@staticmethod
def static_method():
print("静的メソッド")
@classmethod
def class_method(cls):
print(cls.__name__)
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@staticmethod
def static_method():
print("静的メソッド")
@classmethod
def class_method(cls):
print(cls.__name__)
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
複数デコレータのスタック
複数のデコレータは定義段階で下から上へ順に関数をラップします,呼び出し段階で上から下へ順に実行します:
例
def decorator1(func):
def wrapper():
print("Decorator 1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2")
func()
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
def wrapper():
print("Decorator 1")
func()
return wrapper
def decorator2(func):
def wrapper():
print("Decorator 2")
func()
return wrapper
@decorator1
@decorator2
def say_hello():
print("Hello!")
say_hello()
最終出力:
Decorator 1 Decorator 2 Hello!
核心まとめ
デコレータ = 関数を包む関数 + 元のコードを変更せずに機能を拡張する
- @構文の本質は関数の置き換え
- wrapperこそが実際に実行される関数
- 汎用性を高めるために *args, **kwargs の使用を推奨
- 関数、クラス、さらには引数を持つデコレータにも対応