天天看點

python裝飾器入門

python裝飾器入門

目錄

  • 概念:
    • 簡單地說:

      他們是為其他函數的新增功能的函數

  • 原則 :
    • 不修改被裝飾函數的源代碼
    • 不修改被裝飾函數的調用方式
  • 優點:
    • 有助于讓我們的代碼更簡短,也更Pythonic(Python範兒
  • 應用場景:
    • 在項目疊代過程中,需要不停的為某一個功能(函數)新增或删除某些小功能, 如果可複用的函數本來比較多, 在如果不停的對原始函數進行修改, 這将會是一件煩躁和重複的工作. 而使用裝飾器就可以很完美的解決這種問題, 如web使用者的的授權管理,日志輸出等!

  • 函數即變量
    • 函數本質在計算機的記憶體中的一個單獨記憶體空間, 函數名如同其他類型(int, str, list...)變量名一樣,可以進行指派,傳遞
  • 嵌套函數 :
    • 定義: 在一個函數中定義另一個函數:
      def hi(name="faily"):
          print("now you are inside the hi() function")
      
          def greet():
              return "now you are in the greet() function"
      
          def welcome():
              return "now you are in the welcome() function"
      
          print(greet())
          print(welcome())
          print("now you are back in the hi() function")
      
      if __name__ == "__main__":
          hi()           
  • 從函數中傳回函數
    • 其實并不需要在一個函數裡去執行另一個函數,也可以将其作為輸出傳回出來
      def hi(name="faily"):
          def greet():
              return "now you are in the greet() function"
      
          def welcome():
              return "now you are in the welcome() function"
      
          if name == "faily":
              return greet
          else:
              return welcome
      
      if __name__ == "__main__"
          a = hi()        # 此時傳回的a實際是greet函數名
          print(a)        # 執行greet函數           

      if/else

      語句中我們傳回

      greet

      welcome

      ,而不是

      greet()

      welcome()

      。為什麼那樣?這是因為當你把一對小括号放在後面,這個函數就會執行;然而如果你不放括号在它後面,那它可以被到處傳遞,并且可以指派給别的變量而不去執行它。
      當我們寫下

      a = hi()

      hi()

      會被執行,而由于

      name

      參數預設是yasoob,是以函數

      greet

      被傳回了。如果我們把語句改為

      a = hi(name = "momo")

      ,那麼

      welcome

      函數将被傳回。我們還可以列印出

      hi()()

      ,這會輸出now you are in the greet() function。
  • 高階函數
    • 定義: 把一個函數名當作實參傳遞給另外一個函數
      # 計算函數的bar運作時間
      import time
      
      def bar():
          time.sleep(3)
          print("in the bar")
      
      def run_time(func):
          t1 = time.time()
          func()
          t2 = time.time()
          print("the func:{0} run time is {1}".format(func.__name__,t2-t1))
      
      if __name__ == "__main__":
          run_time(bar)           
    • 缺點: 雖然實作了此功能,但是改變了 

      bar

      函數名

  • 實際就是: 

    高階函數

     +

    嵌套函數

    的結合體

    認證 裝飾器演變

    • v1 将函數作為參數傳遞進 嵌套函數中, 傳回函數本身
      def cert_decorator(func):
      
          def wrapTheFunction():
              user = input("pls input password:").strip()
              if user == "faily":
                  print("---welcome login----")
                  func()
              else:
                  print("----wrong password")
          return wrapper
      
      def task():
          print("do somthing ")
      
      if __name__ == "__main__":
          task = cert_decorator(task)   # 執行裝飾器函數,将task作為參數傳遞,指派給task變量,傳回的是wrapper函數名,内部函數func并沒有執行
          task()                        # 執行task,實際就是執行了wrapper(),然後執行了内部的func()函數           
      實作了不改變函數名的和内部結構的前提下, 實作了認證,但是, 但是... 使用者需要多些一行重命名函數變量的方式
    • v2 python内部可以通過

      @

      符号進行直接調用
      def cert_decorator(func):
      
          def wrapTheFunction():
              user = input("pls input password:").strip()
              if user == "faily":
                  print("---welcome login----")
                  func()
              else:
                  print("----wrong password")
          return wrapTheFunction
      
      @cert_decorator
      def task():
          print("do somthing ")
      
      if __name__ == "__main__":
          task()           
      ※ 好像我們大功告成了... 别高興得太早 ,如果我們運作一下這行代碼 會存在一個問題
      print(task.__name__)
      #output
      wrapTheFunction             
      . 這并不是我們想要的!Ouput輸出應該是

      task

      。這裡的函數被warpTheFunction替代了。它重寫了我們函數的名字和注釋文檔(docstring), 如果我們想在被裝飾的函數在執行之前去通路函數的某些屬性(文檔, 變量名...),顯然是沒辦法做到的, 怎麼辦???

      幸運的是Python提供給我們一個簡單的函數來解決這個問題,那就是

      functools.wraps

      。我們修改上一個例子來使用

      functools.wraps

    • v3 使用functools.wraps
      from functools import wraps
      
      def cert_decorator(func):
          @wraps(func)
          def wrapTheFunction():
              user = input("pls input password:").strip()
              if user == "faily":
                  print("---welcome login----")
                  func()
              else:
                  print("----wrong password----")
          return wrapTheFunction
      
      @cert_decorator
      def task():
          print("do somthing ")
      
      if __name__ == "__main__":
          task()
          print(task.__name__)
      
      
      # output
      pls input password:mm
      ----wrong password----
      task           
      注意:@wraps接受一個函數來進行裝飾,并加入了複制函數名稱、注釋文檔、參數清單等等的功能。這可以讓我們在裝飾器裡面通路在裝飾之前的函數的屬性。
    • 作者: @failymao 本文為作者原創,轉載請注明出處: https://www.cnblogs.com/failymao/p/10454247.html