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("元の関数を実行")

解説:

  • 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()

出力:

函数执行前
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")

出力:

执行前
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()

説明:これは「デコレータファクトリー」で、外側の関数が引数を受け取ります。

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 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)
初始化
True

組み込みデコレータ

よく使われる組み込みデコレータ:

  1. @staticmethod:静的メソッドを定義
  2. @classmethod:クラスメソッドを定義
  3. @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

複数デコレータのスタック

複数のデコレータは定義段階で下から上へ順に関数をラップします,呼び出し段階で上から下へ順に実行します:

例

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()

最終出力:

Decorator 1
Decorator 2
Hello!

核心まとめ

デコレータ = 関数を包む関数 + 元のコードを変更せずに機能を拡張する
  • @構文の本質は関数の置き換え
  • wrapperこそが実際に実行される関数
  • 汎用性を高めるために *args, **kwargs の使用を推奨
  • 関数、クラス、さらには引数を持つデコレータにも対応
その他の拡張