0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonでデコレータを使用する

Last updated at Posted at 2023-11-08

初めに

デコレーターは、関数を引数として受け取り、それを修飾(拡張または変更)してから返す別の関数です。
主に関数の再利用性を高め、コードをより読みやすく、管理しやすくするために使用されます。

デコレーターの基本

def my_decorator(func):
    def wrapper():
        # 何か前処理
        func()
        # 何か後処理
    return wrapper

my_decorator はデコレータです。これは、func という関数を引数として受け取り、wrapper という新しい関数を返します。wrapper 関数内で、func が呼び出される前後に追加のコード(前処理や後処理)を実行することができます。

デコレーターを使用する
@my_decorator
def my_function():
    print("Hello from my function!")

この例では、my_function 関数は my_decorator でデコレートされます。つまり、my_function を呼び出すときに実際には wrapper 関数が呼び出され、my_function の中身は wrapper 関数の中から呼び出されます。

引数と戻り値を持つデコレータ

引数や戻り値を持つ関数にデコレータを適用する場合、wrapper 関数内で *args と **kwargs を使用して、任意の数の位置引数やキーワード引数を受け取るようにします

def my_decorator(func):
    def wrapper(*args, **kwargs):
        # 何か前処理
        result = func(*args, **kwargs)
        # 何か後処理
        return result
    return wrapper

デコレータは関数の実行時間を計測したり、ログを出力したり、キャッシュやエラーハンドリングを行ったりと多様な用途に使用できます。
例えば、関数の実行時間を計測するデコレータは次のようになります。

使用例
import time

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

@timing_decorator
def my_function():
    # 長い処理
    pass

このデコレータを使うことで、任意の関数の実行時間を簡単に計測できます。

各関数が実行される前に、特定の属性があるかチェックを行うデコレーター

def decorate_all_methods(method_decorator: Callable) -> Callable:
     def class_decorator(cls):
         for name, obj in vars(cls).items():
             # コンストラクタ以外のメソッドをデコレートする
             if callable(obj) and name != "__init__":
                 setattr(cls, name, method_decorator(obj))
         return cls
     return class_decorator
	 
 
 def check_logger(method: Callable) -> Callable:
     @wraps(method)
     def wrapper(*args, **kwargs):
         _self = args[0]
         # _self._loggerがセットされていない場合はdebugログを出す
         if _self._logger is None:
             print(
                 "Logger instance isn't set on the Module. "
                 "Please use it via ModuleHandler")
             raise Exception
         res = method(*args, **kwargs)
         return res
     return wrapper

decorate_all_methodsクラス

このクラスはデコレーターを引数に取ります。
デコレーター対象の呼び出し可能オブジェクト、つまりメソッドに対して、setattrで引数のデコレータを追加しています。

つまり、setattrによって、クラス内の各メソッドにデコレータが適用され、元のメソッド名でアクセスした際にデコレータを介して実行されるようになります。

check_logger

追加したいデコレーターの機能を定義しています。
decorate_all_methodsクラスでメソッド追加時には、wrapper関数を返しているので、check_logger内のwrapper関数がメソッドに追加されることになります。
その追加されたwrapper関数は_self._logger属性が存在することを確認し、なければエラーを出しています。
存在する場合はmethod(*args, **kwargs)で実際のメソッドが実行されます。

また@wrapsでは、@wrapsでラップされた関数が、オリジナル(実際)の関数のメタデータを保持できるようになります。
今回だとmethod(*args, **kwargs)のmethodは実際のメタデータが含まれているので実際のメソッドを実行することができるのです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?