分かりやすい理解pythonアクセサリー

19046 ワード

例を挙げる
def play():
   print("      ")
   sleep(3)
   print("    ")

プレイにかかる時間を計算したいのですが
def play():
   start = time.time()
   print("      ")
   sleep(3)
   print("    ")
   end = time.time()
   print("  {:.2f}".format(end - start))

他の類似関数の時間を計算したい場合はどうすればいいですか?計算関数の時間を関数にカプセル化して冗長性の問題を解決することができます
def cal_time(func):
   start = time.time()
   func()
   end = time.time()
   print("  {:.2f}".format(end-start))

これで冗長性の問題は解決しましたが、新しい問題があり、時間を計算するにはcal_を呼び出さなければならないと思います.timeメソッドはplayなどの時間需要自体を計算する関数を呼び出して重複冗長問題を解決する方法がありますか?これが次の装飾器です
def cal_time(func):
   def wrapper():
      start = time.time()
      func()
      end = time.time()
      print("  {:.2f}".format(end - start))
   return wrapper #       

@cal_time
def play():
   print("      ")
   sleep(3)
   print("    ")

if __name__ == '__main__':
   play()

装飾器は内蔵関数を定義し,装飾が必要な関数(play)といくつかの自由変数(start,endなど)を結合することで冗長性を解決し,元の関数呼び出し方法を保存する
では、パラメータはどのように伝達されますか?
def cal_time(func):
   def wrapper(*args):
      start = time.time()
      func(*args)
      end = time.time()
      print("  {:.2f}".format(end - start))
   return wrapper #       

@cal_time
def play(game):
   print("       %s" % game)
   sleep(3)
   print("    ")

if __name__ == '__main__':
   play("LOL")


以下は*kwargsパラメータ付きのアクセサリーです
def cal_time(func):
   def wrapper(*args,**kwargs):
      start = time.time()
      func(*args,**kwargs)
      end = time.time()
      print("  {:.2f}".format(end - start))
   return wrapper #       

@cal_time
def play(game,name,t):
   print("%s      %s" % (name,game))
   sleep(t)
   print("    ")

if __name__ == '__main__':
   play("LOL","  ",t=5)

注意:
  • argsはpython位置パラメータからなるメタグループであり、*argsは解凍メタグループ
  • である.
  • kwargsはpython位置パラメータからなる辞書で、kwargs.items()は
  • を遍歴する
  • 関数パラメータ(play)が通常のパラメータのみ(位置パラメータを含まない)の場合、装飾器に*kwargsを使用する必要はありません.そうしないと、パラメータ*kwargs
  • が必要です.
    関数に戻り値がある場合、wrapperは対応する関数の戻り値を返さなければなりません.そうしないとNoneを返します.
    def cal_time(func):
       def wrapper(*args, **kwargs):
          start = time.time()
          res = func(*args, **kwargs)
          end = time.time()
          print("  {:.2f}".format(end - start))
          return res #    
    
       return wrapper  #       
    
    
    @cal_time
    def play(game, name, t):
       print("%s      %s" % (name, game))
       sleep(t)
       print("    ")
       return name #    
    
    
    if __name__ == '__main__':
       username = play("LOL", "  ", t=5)
       print(username)

    例のまとめ:play関数の実行フローをよく観察すると、実は実行内部のwrapper関数であり、(1)wrapper内部が他の自由変数(さらには関数)(2)内部のfuncを増幅して関数の重複冗長問題を解決することができることにほかならない.このfuncはここでplay関数を指し、任意の関数に置き換えることができる.
    次は別の例です
    import functools
    
    def log(func):
       @functools.wraps(func)
       def wrapper(*args, **kwargs):
          print('call %s():' % func.__name__)
          print('args = {}'.format(*args))
          return func(*args, **kwargs)
    
       return wrapper
    
    @log
    def test(p):
       print(test.__name__ + " param: " + p)
    
    if __name__ == '__main__':
       test("I'm a param")

    アクセサリーを使う時、@文法を使っていて、ちょっと困りました.実は、装飾器はただの方法で、次の呼び出し方法とは違いません.
    def test(p):
        print(test.__name__ + " param: " + p)
    
    wrapper = log(test)
    wrapper("I'm a param")

    @文法はただ関数を装飾器関数に伝えるだけで、不思議なところはありません.注目すべきは@functools.wraps(func)はpythonが提供する装飾器です.元の関数のメタ情報を装飾器のfunc関数にコピーすることができます.関数のメタ情報にはdocstring、name、パラメータリストなどが含まれます.@functoolsを削除してみてください.wraps(func)ではtest._name__の出力がwrapperになりました.
    リファレンス
    pythonキーワードパラメータpython装飾器