Python Decorator Basics



知っておくべきことは...
  • Pythonでは、関数はオブジェクトです.すなわち,関数は他のオブジェクトのように他の関数のパラメータとしてもよい.
  • 関数内で関数を定義するか、新しく定義した関数を返すことができます.ただし、親関数が実行されるまで内部関数は定義されず、親関数のローカルスキャン内でのみ実行できます.
  • デコレーション


    簡単に言えば、Pythonレコーダは、ある関数(wrap)を囲むことによって関数の動作を変更する関数であり、PythonはSyntatic Sugarを追加することによってレコーダをターゲット関数により容易に適用する.
    def my_decorator(func):
      def my_wrapper():
        # Wrapper는 데코레이터가 받은 함수의 행동 또는 인자를 수정한다.
        # 이 경우 원 함수를 두 번 실행한다.
        func()
        func()
      # 데코레이터는 Wrapper를 리턴한다.
      return my_wrapper
    
    # 데코레이터가 적용된 함수는 사실 my_wrapper(say_hello)와 같다.
    @my_decorator
    def say_hello():
        print("Hello?")
            
    >> say_hello() # 이 실행은 사실 my_wrapper(say_hello)()와 같다.
    "Hello?"
    "Hello?"

    円関数に入力パラメータが含まれている場合:


    ソース関数に入力パラメータがある場合は、データレコーダのラッパ関数を同じパラメータを受け入れるように設定する必要があります.
    ただし、この場合、制限デコーダのターゲット関数は、同じ数のパラメータを受け入れる関数のみを修正することができる.
    解決策は、包装関数に*args**kwargsパラメータを適用することである.
    def my_decorator(func):
      def my_wrapper(*args, **kwargs):
        # wrapper가 받은 임의의 인자를 그대로 원 함수에 전달한다.
        func(*args, **kwargs)
        func(*args, **kwargs)
    
      return my_wrapper
    
    @my_decorator
    def say_hello():
        print("Hello?")
    
    @my_decorator
    def say_name(name):
      print(f"Hello {name}?")
      
    >> say_hello()
    "Hello?"
    "Hello?"
    
    >> say_name("Jun")
    "Hello Jun?"
    "Hello Jun?"

    円関数の戻り値を返します


    簡単に言えば、包装器は必要な関数を実行し、結果を一緒に返すことができる.
    def my_decorator(func):
      def my_wrapper(*args, **kwargs):
        # wrapper가 받은 임의의 인자를 그대로 원 함수에 전달한다.
        func(*args, **kwargs)
        return func(*args, **kwargs)
    
      return my_wrapper

    元の関数のメタデータ情報を保持


    関数がレコーダによってカプセル化されると、関数のメタデータ情報は関数を指さなくなります.
    この場合、functoolバージョンライブラリで定義されたdecorator @functool.wrapsを使用して、decorator定義でパッケージを装飾します.
    たとえば、
    def my_decorator(func):
      def my_wrapper(*args, **kwargs):
        func(*args, **kwargs)
        func(*args, **kwargs)
    
      return my_wrapper
      
    import functools
    def better_decorator(func):
      @functools.wraps(func)
      def my_wrapper(*args, **kwargs):
        func(*args, **kwargs)
        func(*args, **kwargs)
    
      return my_wrapper
    
    
    @my_decorator
    def say_name(name):
      print(f"Hello {name}?")
    
    @my_decorator2
    def say_name2(name):
      print(f"Hello {name}?")
      
    >> say_name.__name__
    'my_wrapper'
    
    >> say_name2.__name__
    'say_name2'

    Reference

  • https://realpython.com/primer-on-python-decorators/