Traffine I/O

日本語

2023-02-24

Pythonのデコレータについて

デコレータとは

Pythonのデコレータは、ソースコードを変更せずに関数やクラスの振る舞いを修正または拡張することができる強力な機能です。デコレータは、別の関数またはクラスを入力として取り、変更されたバージョンを返す関数です。

Pythonでは、デコレータは@記号に続いてデコレータ関数の名前を付けて表現されます。デコレートされた関数またはクラスが呼び出されると、実際には元の関数またはクラスの変更されたバージョンが呼び出されています。

デコレータは、ログ出力やタイミング機能の追加、認証または承認の強制、結果のキャッシュなど、様々なタスクを実行するために使用できます。特に、複数の関数またはクラスに再利用可能な方法で振る舞いを修正する必要がある場合に便利です。

デコレータの使い方

Pythonでデコレータを使う方法についての手順は次のとおりです。

  1. デコレータ関数を定義する
    最初に、デコレートに使用する関数を定義します。この関数は、関数またはクラスを引数として受け取り、修正されたバージョンを返します。

  2. デコレートする関数またはクラスを定義する
    次に、デコレートしたい関数またはクラスを定義します。この関数またはクラスは、デコレータ関数より前に定義する必要があります。

  3. デコレータを関数またはクラスに適用する
    デコレータを関数またはクラスに適用するには、@記号に続いてデコレータ関数の名前を使用します。これを、デコレートする関数またはクラスの定義の直前の行に配置します。

  4. デコレートされた関数またはクラスを呼び出す
    最後に、通常の関数またはクラスを呼び出す方法と同様に、デコレートされた関数またはクラスを呼び出します。

以下はPythonでデコレータを使用する例コードです。

python
def my_decorator(func):
    def wrapper():
        print("Before the function is called.")
        func()
        print("After the function is called.")
    return wrapper

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

say_hello()

このコードでは、関数を引数として受け取り、修正されたバージョンを返すデコレータ関数my_decoratorを定義しています。修正されたバージョンは、元の関数が呼び出される前後にいくつかの追加動作を追加します。

次に、@記号を使用して、say_hello関数にデコレータを適用します。これは、say_helloが呼び出されると、my_decoratorデコレータによって作成された関数の修正版が実際に呼び出されることを意味します。

このコードを実行すると、次の出力が得られます。

python
Before the function is called.
Hello, world!
After the function is called.

これは、デコレータがsay_hello関数が呼び出される前後にいくつかの追加動作を追加することに成功したことを示しています。

デコレータのチェーン

デコレータのチェーンは、Pythonで複数のデコレータを関数やクラスに適用できる強力なテクニックです。これにより、元の関数やクラスを変更することなく、コードに異なるレイヤーの機能を追加できます。

Pythonでは、デコレータはリストされた順序で適用されます。関数に複数のデコレータが適用されると、デコレートされた関数のチェーンが生成されます。チェーン内の各デコレータは、前のデコレータの出力を変更します。

デコレータのチェーンは、ログ記録、タイミング、キャッシング、認証など、様々なタスクを実行するために使用できます。いくつかの機能を簡単に組み合わせ、わずか数行のコードで複雑な振る舞いを作成できます。

以下は、Pythonでデコレータのチェーンを使用する例です。

python
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 my_function():
    print("Original function")

my_function()

この例では、2つのデコレータdecorator1decorator2を定義し、my_function関数に@シンボルを使用して両方のデコレータを適用しています。

このコードを実行すると、次の出力が得られます。

python
Decorator 1
Decorator 2
Original function

これは、リストされた順序でデコレータが適用され、decorator1が最初に適用され、次にdecorator2が適用されたことを示しています。各デコレータの出力がチェーン内の次のデコレータに渡され、最終的に元の関数の出力が生成されます。

デコレータのチェーンは、Pythonでよりクリーンでモジュール化されたコードを書くのに役立つ強力なテクニックです。複数のデコレータを組み合わせることで、理解し、保守しやすい複雑な振る舞いを作成できます。

Python の組み込みデコレータ

Pythonの組み込みデコレータは、関数やクラスの振る舞いを変更するために使用できる事前定義されたデコレータです。これらのデコレータはPython言語自体によって提供され、追加のコードを必要とせずに使用することができます。

Pythonでもっとも一般的に使用される組み込みデコレータには、@staticmethod@classmethod@property@abstractmethodなどがあります。

@staticmethodデコレータは、クラス内の静的メソッドを定義するために使用されます。静的メソッドとは、クラスのインスタンスではなく、クラス自体に属するメソッドのことです。

@classmethodデコレータは、クラス内のクラスメソッドを定義するために使用されます。クラスメソッドとは、クラスにバインドされており、クラスのインスタンスにバインドされていないメソッドのことです。

@propertyデコレータは、クラス内のプロパティを定義するために使用されます。プロパティは、シンプルな属性アクセスのように見えますが、実際には裏でメソッドを実行する方法でオブジェクトの属性にアクセスおよび変更を許可します。

@abstractmethodデコレータは、クラス内の抽象メソッドを定義するために使用されます。抽象メソッドとは、実装がない宣言されたメソッドのことです。これらは、抽象メソッドを実装するクラスのサブクラスによってオーバーライドされることを意図しています。

Pythonのビルトインデコレータを使用することで、追加のコードを書かずに関数やクラスの振る舞いを簡単に変更できます。これにより、よりモジュラーでメンテナブルなコードを書くことができます。

デコレータの一般的な使用例

デコレータは、Pythonで関数やクラスの動作を変更することができる強力なツールです。ログ記録、タイミング、キャッシュ、認証など、幅広いタスクに使用できます。この記事では、Pythonでデコレータを一般的に使用する方法について説明します。

ログ記録

デコレータを使用して、関数の入力と出力を記録することができます。これはデバッグや関数の動作を理解するために役立ちます。以下は、ログ記録デコレータの例です。

python
def log(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"Finished {func.__name__} with result={result}")
        return result
    return wrapper

@log
def my_function(x, y):
    return x + y

my_function(1, 2)
python
Calling my_function with args=(1, 2), kwargs={}
Finished my_function with result=3

タイミング

別の一般的な使用例は、関数が実行される時間を計測することです。これは、遅いまたは非効率的な関数を特定するために役立ちます。以下は、タイミングデコレータの例です。

python
import time

def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.2f} seconds")
        return result
    return wrapper

@timeit
def my_function():
    time.sleep(1)

my_function()
python
my_function took 1.00 seconds

キャッシュ

別の一般的な使用例は、関数の結果をキャッシュしてパフォーマンスを改善することです。これは、計算に時間がかかる関数や、コストの高いI/O操作が必要な関数に役立ちます。以下は、キャッシュデコレータの例です。

python
def cache(func):
    cached_results = {}

    def wrapper(*args):
        if args in cached_results:
            return cached_results[args]
        result = func(*args)
        cached_results[args] = result
        return result

    return wrapper

@cache
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(30))

This will output:

python
832040

認証

4つ目の一般的なデコレータの使い方は、関数の認証や認可要件を強制することです。これは、機密性の高い関数の保護やリソースへのアクセス制御に役立ちます。以下は認証用のデコレータの例です。

python
def requires_authentication(func):
    def wrapper(*args, **kwargs):
        if not is_authenticated():
            raise Exception("Not authenticated")
        return func(*args, **kwargs)
    return wrapper

@requires_authentication
def my_function():
    # This function requires authentication
    pass

参考

https://www.geeksforgeeks.org/decorators-in-python/
https://peps.python.org/pep-0318/

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!