1. 程式人生 > >python入門基礎-函數裝飾器的理解

python入門基礎-函數裝飾器的理解

不改變 函數名 result 原本 都沒有 而且 升級 結束 log

1.裝飾器

# 知乎某大神是這麽比喻裝飾器的:內褲可以用來遮羞,但是到了冬天他就沒有辦法為我們禦寒,聰明的人於是發明了長褲,有了長褲後寶寶再也不冷了,
# 裝飾器就像我們這裏說的長褲,在不影響內褲作用的前提下,給我們的身子提供了保暖的功效。
#
# 大神是將程序中原本的函數比喻成內褲,而裝飾器比喻成了長褲,這樣在寒冬裏它們結合一起使用就給所有人帶來了溫暖。
#
# 裝飾器本質上是一個python函數,它可以讓其它函數在不改動代碼的情況下增加額外的功能,並且裝飾器的返回值也是一個函數對象。
# 在很多的應用場景中我們都可以用到裝飾器,比如:插入日誌、性能測試、事務處理等,正因為有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼,並且繼續的重用。
# 那python的裝飾器可以概括理解為:裝飾器的作用就是為已經存在的對象增加格外的功能,並且不改變原函數的調用方式。

2.裝飾器的過程

#首先我們看一段代碼例子。

def func():
    print(hello word!)

func()  #我們調用函數後,可以正常打印出來 hello Word!

#比如現在我們有了一個新的需求,要在函數中增加打印函數的執行時間,那添加後的代碼就是下面這段了。

import time     #加載time模塊
def func():
    start = time.time() #聲明變量記錄函數開始執行的時間
    print
(hello word!) end = time.time() #聲明變量記錄函數結束執行的時間 result = end - start #聲明變量用於接收整個函數運行所耗時間 print(result) #打印出來函數執行的總體耗時,當前現在函數中沒有什麽過多的復雜代碼,所以基本不存在耗時多少 #對於上面的需求我們已經實現了,但是我們是改變原函數體的結構實現的,這樣在正常的開發環境中肯定是不行的,所以代碼優化成下面這種。 import time def timer(): start = time.time() func() end
= time.time() result = start - end print(result) def func(): print(hello word!) timer() #這樣的話,我們就在不改變原函數體的結構情況下,增加了一項打印函數執行耗時的功能,難道這就是裝飾器了?NO、NO、NO 還請繼續往下看

2.1簡單的裝飾器

#前面我們也在沒有更改原函數體的基礎上增加了函數執行耗時的打印,那為什麽不叫裝飾器呢?因為隨便沒有改變函數體的結構,
# 但是我們改變了函數名的調用,之前由func()去調用就可以打印hello word,現在是調用timer()函數才能打印,所以我們的代碼需要更進一步的調整。

import time

def timer(f): #實際f是func函數名
    def inner():
        start = time.time()
        f()   #實際調用的是func()
        end = time.time()
        result = start - end
        print(result)
    return inner  #將inner函數名返回

def func():
    print(hello word!)

func = timer(func)      #此時是 func = inner
func()      #此時實際調用的函數是  inner()

#那上面這行代碼就可以稱之為一個簡單的函數體了,但是也不是很完美,因為真實的環境中肯定存在很多的函數,在大多函數都需增加函數執行耗時功能的時候。
#我們不可能在沒有個函數調用前,都提前像 func = timer(func)這種聲明,所以我們就要牽出另一個強大的功能“@語法糖
#@符號是裝飾器的語法糖,在定義函數的時候使用,可以避免再一次賦值操作,就是說用了@語法糖可以節省上面的func = timer(func)這步操作。

import time

def timer(f):   #參數f 就是 func 或 func2 函數名
    def inner():
        start = time.time()
        f()     #實際調用的是 func 或 func2函數
        end = time.time()
        result = start - end
        print(result)
    return inner    #將inner函數名返回
@timer   #此時是 func = inner
def func():
    print(hello word!)

@timer      #此時是 func2 = inner
def func2():
    print(is func2)

func()
func2()

#上面通過@語法糖節省了func2 = inner這個過程,以後就可以在需要增加打印耗時的函數上面聲明語法糖就可以了。
#上面的原函數調用都沒有帶上實參,那如果原函數帶上了不同的實參,上面的方法肯定行不通了,所以我們需要進一步升級,這裏又要用一個強大的功能了。
#要用的功能就是 *args 和 **kwargs 兩個動態參數,並且要結合在一起使用,這樣就可以天下無敵了

import time

def timer(f):   #參數f 就是 func 或 func2 函數名
    def inner(*args,**kwargs):
        start = time.time()
        f(*args,**kwargs)     #實際調用的是 func 或 func2函數
        end = time.time()
        result = start - end
        print(result)
    return inner    #將inner函數名返回
@timer   #此時是 func = inner
def func(a,b):
    print(hello word!,a,b)

@timer      #此時是 func2 = inner
def func2(a,b,c,d):
    print(is func2,a,b,c,d)

func(1,2)
func2(1,2,3,4)

#那上面的這個裝飾器基本可以算作完善了

3.開放封閉原則

#函數在什麽時候是開放的和什麽時候是關閉的呢?聽我娓娓道來
#(1)對擴展是開放的
    #為什麽要對擴展開放呢?
    #任何一個程序,不可能在設計之初就已經想好了所有功能,而且後面不需要做任何的更新和修改,所以我們必須允許代碼的擴展和添加新功能。

#(1)對修改是封閉的
    #為什麽要對修改封閉呢?
    #因為我們寫一個函數,很有可能影響其他已經在使用這個函數的用戶,所以對修改函數是封閉的。

#裝飾器完美的遵循了這個開放封閉原則。

4.裝飾器的主要功能和固定結構

#(1)裝飾器的主要功能
#在不改變函數調用方式的基礎上在函數的前後添加功能。

#(2)裝飾器的固定格式
def timer(func):    #裝飾器函數,func是被裝飾的函數名
    def inner(*args,**kwargs):
        ‘‘‘在被裝飾函數之前要做的操作‘‘‘
        ret = func(*args,**kwargs)  #調用被裝飾的函數
        ‘‘‘在被裝飾函數之後要做的操作‘‘‘
        return ret
    return inner

python入門基礎-函數裝飾器的理解