Decorator——Python初級函數裝飾器

最近想整一整數據分析,在看一本關於數據分析的書中提到了(1)if __name__ == '__main__' (2)列表解析式 (3)裝飾器。html

先簡單描述一下前兩點,再詳細解說Python初級的函數裝飾器。python

進入正題:app

 

 

1、if __name__ == '__main__' 函數

  1. 首先,__name__是一個程序名變量,而這個變量的值是根據程序的運行方式決定的。若是程序是被看成主程序運行的,那__name__將會被賦值爲__main__;當程序是做爲模塊被其餘文件調用的,那它會自動被賦值爲模塊所在(程序)的文件名。
  2. 通常這句代碼以這種形式出現:
    1 def printHello():
    2     print("Hello World!")
    3     print(__name__)
    4 if __name__ == '__main__':
    5     printHello()

    其輸出以下:測試

    Hello World!
    __main__

    由於此時這個文件是以主程序的方式運行的,故它的__name__爲__main__。spa

  3. 這個 if 判斷語句存在的意義在於當程序被做爲模塊引入其餘文件時,一句import會使得這個模塊自動運行一次。
  4. 故當 printHello 函數被做爲模塊調入其餘程序文件時,即:
    1 from name_main import printHello
    2 printHello()

    咱們只會獲得 printHello 運行一次的結果。這一次結果是 import 語句的效果,真正的 printHello 函數被 if 語句攔住了(由於它的__name__變成了本身的文件名而非__main__)。code

 

 

2、列表解析式htm

  • 列表解析式其實就是一種根據已有列表,高效建立新列表的方式。
  • 一般形式如:a = [ i for i in list1 if i%2==0 ]
  • 學會使用,能夠簡潔化代碼。

 

 

3、裝飾器對象

  • 裝飾器器如其名,起到了「裝飾」的做用。體如今代碼中就是給原程序添加「裝飾」,添加新的東西。裝飾器的本質是函數。
  • 裝飾器通常用於裝飾函數和類。
  • 有人問既然裝飾器起到的是裝飾的做用,那爲何不在函數中就添加這些「裝飾」?固然能夠,這確實可行。但設想咱們須要每一個函數打印本身的運行時間,那咱們就須要在每個函數裏記錄開始與結束時間,再相減獲得時間差(即運行時間)。這會使得函數很是的「臃腫」,並且若是有成百上千個函數都須要這個功能,那一個一個函數加代碼的措施就顯得十分「無心義」。
  • 但經過裝飾器:咱們簡單地定義一個「裝飾」函數去實現須要的功能,而後在執行函數的前面加上一句「@裝飾函數」,就實現了對函數們的裝飾和升級。
  • 舉一個例子,咱們如今有兩個函數分別實現打印 hello 和 goodbye,咱們須要每次打印時同時把這個函數名打印出來。咱們就能夠用裝飾器來完成,上代碼。
    #直接裝飾@
    def printname(func):        #裝飾器:實質是一個函數,參數是須要裝飾的函數名(非函數調用)
        def wrapper(*args, **kwargs):    #可變參數*args和關鍵字參數**kwargs
            print(f"[DEBUG]: enter {func.__name__}()")
            return func(*args, **kwargs)
        return wrapper          #裝飾器函數返回的是裝飾完的函數
    
    
    @printname
    def say_hello():
        print("hello!")
        
    
    @printname
    def say_goodbye():
        print("goodbye!")
        
    
    if __name__ == '__main__':
        say_hello()
        say_goodbye()

    程序運行結果爲:blog

    [DEBUG]: enter say_hello()
    hello!
    [DEBUG]: enter say_goodbye()
    goodbye!

    成功實現每次打印hello時打印出函數名稱say_hello()。

  • 接下來是對裝飾器的深層探討。裝飾器的本質是函數,那咱們若是不用@的裝飾方式,看看怎麼樣實現一樣的功能。
    #間接裝飾
    def printname(func):        #裝飾器:實質是一個函數,參數是須要裝飾的函數名(非函數調用)
        def wrapper(*args, **kwargs):    #可變參數*args和關鍵字參數**kwargs
            print(f"[DEBUG]: enter {func.__name__}()")
            return func(*args, **kwargs)
        return wrapper          #裝飾器函數返回的是裝飾完的函數
    
    
    def say_hello():
        print("hello!")
        #return 0           測試line:23
    
    def say_goodbye():
        print("goodbye!")
    
    if __name__ == '__main__':
        decorator = printname(say_hello)
    
        '''
        不能使用decoratot = printname(say_hello());
        多是由於調用函數的名稱是用func.__name__,而非func().__name__
        用了func().name會報錯'NoneType'對象沒有'__name__'屬性。
    
        由於say_hello()函數沒有return,故執行結果爲NoneType無。
        '''
    
        decorator()
    
        '''
    
        接上綠色註釋:
        >>> type(say_hello())
        hello!
        <class 'NoneType'>
        >>> type(say_hello)
        <class 'function'>
    
        
        '''
    #func,函數不帶括號時,調用的是這個函數自己。是一整個函數體,不須等函數執行完成。
    
    #func(),函數帶括號時,調用的是函數的執行結果,須要等待函數執行完成的結果。

    和@方法的區別主要在於如何使用裝飾器函數。咱們須要在調用時把執行函數傳入給裝飾函數,return 的結果返回給新函數;再運行新函數,就實行了這一效果。

  • 最後對裝飾函數的代碼作一個解讀。(應該在前面解讀的)
  1. printname(func)是裝飾函數,它的參數是func(一個函數,即執行函數),它的return是wrapper函數。
  2. 在printname裏定義一個wrapper函數(參數、關鍵字可變),把裝飾器的內容包裝在wrapper裏。wrapper返回的是func(執行函數)。
  3. 至關因而這一段代碼執行了wrapper函數
    1 def printname(func):
    2     def wrapper(*args, **kwargs):
    3         print(f"[DEBUG]: enter {func.__name__}()")
    4         return func(*args, **kwargs)
    5     return wrapper

    實現了:print(裝飾)+  func(執行函數),即成功裝飾。

  • 還有一個頗有趣的例子,在https://www.cnblogs.com/cicaday/p/python-decorator.html#4027718046,感謝做者的點撥。

 

  • 裝飾器總結:

    實質: 是一個函數

    參數:是你要裝飾的函數名(並不是函數調用)

    返回:是裝飾完的函數名(也非函數調用)

    做用:爲已經存在的對象添加額外的功能

    特色:不須要對對象作任何的代碼上的變更

相關文章
相關標籤/搜索