Python デコレータを使用して関数を強化する方法

Python デコレータは、関数やメソッドを変更または拡張するための強力で柔軟な方法です。デコレータは関数を追加機能でラップする方法を提供し、実際のコードを変更することなく関数の動作を拡張できます。この記事では、デコレータの概念、作成方法と使用方法、および実用的な例をいくつか紹介します。

デコレータとは何ですか?

デコレータは、別の関数を受け取り、明示的に変更することなくその動作を拡張する関数です。Python では、デコレータは、既存の関数やメソッドにログ記録、アクセス制御、パフォーマンス測定などの機能を追加するためによく使用されます。デコレータは、@decorator_name 構文を使用して関数に適用されます。

# Basic example of a decorator
def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

デコレータの仕組み

関数にデコレータを適用すると、Python は基本的に次の手順を実行します。

  1. デコレータ関数は、元の関数を引数として呼び出されます。
  2. デコレータ関数は、元の関数の動作を強化または変更する新しい関数 (多くの場合、wrapper と呼ばれます) を定義します。
  3. デコレータ関数は新しい関数を返します。
  4. デコレートされた関数が呼び出されると、実際にはデコレータによって返された新しい関数が呼び出されます。

シンプルなデコレータの作成

関数の実行時間を測定する簡単なデコレータを作成しましょう。これはパフォーマンス テストと最適化に役立ちます。

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Execution time: {end_time - start_time} seconds")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(2)
    print("Function finished!")

slow_function()

引数付きデコレータの使用

場合によっては、デコレータに引数を渡したいことがあります。これを実現するには、デコレータ ファクトリ (デコレータを返す関数) を作成する必要があります。以下は、引数を取ってカスタム メッセージを指定するデコレータの例です。

def custom_message_decorator(message):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(message)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@custom_message_decorator("Starting the function...")
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

クラス内のメソッドのデコレータ

デコレータは、クラス内のメソッドでも使用できます。一般的な用途としては、メソッド呼び出しのログ記録、アクセス制御、結果のキャッシュなどがあります。デコレータを使用してクラス内のメソッド呼び出しをログに記録する例を次に示します。

def log_method_call(method):
    def wrapper(self, *args, **kwargs):
        print(f"Calling {method.__name__} with arguments {args} and keyword arguments {kwargs}")
        return method(self, *args, **kwargs)
    return wrapper

class MyClass:
    @log_method_call
    def my_method(self, x, y):
        print(f"Result: {x + y}")

obj = MyClass()
obj.my_method(5, 7)

デコレータの連鎖

1 つの関数に複数のデコレータを適用できます。最も内側のデコレータから最も外側のデコレータに適用されます。これにより、さまざまな機能を組み合わせることができます。2 つのデコレータを連鎖させる例を次に示します。

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def exclamation_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + "!"
    return wrapper

@exclamation_decorator
@uppercase_decorator
def greet(name):
    return f"Hello, {name}"

print(greet("Alice"))

結論

デコレータは、関数やメソッドの動作を強化または変更できる Python の多目的ツールです。デコレータを使用すると、関数のコア ロジックを変更することなく、コードベース全体に再利用可能な機能を追加できます。ログ記録、タイミング、出力の変更など、デコレータはコードをクリーンかつ保守しやすい状態に保つのに役立ちます。デコレータの使い方を練習して、より熟練し、Python プロジェクトでその力を活用しましょう。