Python函數篇(5)-裝飾器及實例講解

1.裝飾器的概念

  裝飾器本質上就是一個函數,主要是爲其餘的函數添加附加的功能,裝飾器的原則有如下兩個:python

  • 裝飾器不能修改被修飾函數的源代碼
  • 裝飾器不能修改被修改函數的調用方式  

  裝飾器能夠簡單的理解爲:高階函數+嵌套函數+閉包閉包


2.高階函數

  高階函數我在前面的博客中已經講過了,在這裏我再簡單的說一下吧。
  高階函數:若是一個函數接收的參數是一個函數名,或者返回值是函數名,只要知足任意一個條件,這個函數就稱爲高階函數。app

  • 接收的參數是一個函數名
def foo():
    print("the result from foo")
def bar(func):
    func()
    print("the result from bar")
bar(foo)

  在上面的例子中,我定義了兩個函數,foo()和bar(),在調用bar()函數的時候,我將foo()做爲一個參數傳給了bar(),運行就會獲得foo()的結果,這樣的函數就能夠成爲高階函數。函數

  • 返回值是一個函數名
def foo():
    print("the result from foo")
def bar(func):
    return func
bar(foo)

  調用bar()函數 返回的就是foo()的內存地址,這也能夠稱爲高階函數。code


3.函數嵌套

  函數嵌套:函數嵌套我在前面也已經講過了,其實就是函數的內部再聲明函數,很好理解的一個概念,舉個例子:以下內存

def foo():
    print("the result from foo")
    def bar():
        print("the result from bar")
    bar()
foo()

  在上面這個例子中,我在函數foo()的內部又寫了一個bar()函數,並在下面調用了該函數,這種方式就叫作函數嵌套,能夠嵌套不少層,只要注意函數縮進問題作用域


4.閉包

  在Python中閉包的表現形式能夠理解爲:若是在一個內部函數裏,對在外部做用域(但不是在全局做用域)的變量進行引用,那麼內部函數就被認爲是閉包(closure)。
  單從上面的定義可能很難理解,我下面用一個簡單的程序說明一下:博客

def foo():
    a=1
    b=2
    def bar():
        c=3
        return a+b+c
    return bar
print(foo()())

  在上面這個例子中,bar()就是foo()的一個內部函數,在bar()的局部做用於能夠直接使用foo()的局部變量a,b,簡單的說,這種內部函數能夠訪問外部函數變量的行爲,就叫作閉包。class


4.裝飾器實例

  爲了能讓你們更好的理解裝飾器,我會分步作出這個實例。test

  • 裝飾器的基本實現

  先定義一個函數name(),3秒後打印名字

import time
def name():
    time.sleep(2)
    print("my name is 尼古拉斯趙四")
name()

  如今有一個需求,我想要統計這個函數一個運行了多少秒,在不修改源代碼的狀況下,就須要給這個函數寫一個裝飾器來完成這個需求。

def timmer(func):       #定義一個形參,就是爲了接受name()這個函數,注意前面文章就已經強調過的:函數即變量---func=name
    def wapper():       #定義函數wapper(),用來接收name的參數
        start_time=time.time()
        func()          #實質上就是在運行test()       
        stop_time=time.time()
        print("name()函數一共運行了%s 秒"%(stop_time-start_time))
    return wapper       

import time
@timmer             #使用裝飾器的方法,經過@+做爲裝飾器的那個函數名
def name():
    time.sleep(2)
    print("my name is 尼古拉斯趙四")
name()
運行結果:
my name is 尼古拉斯趙四
name()函數一共運行了2.0002381801605225 秒

  上面這個例子就是一個簡單的裝飾器,沒有修改原函數的調用方法和返回值,裝飾器timmer中用到了高階函數+函數嵌套+閉包的知識,ok,完美。

  • 在裝飾器中添加參數
      上面的例子是完美的實現了所須要的功能,可是問題來了,若是原函數是如今這樣呢?
def name(my_name,my_age):
    time.sleep(2)
    print("my name is %s,my age is %s"%(my_name,my_age))
name("尼古拉斯趙四",18)

  我須要隨機傳入兩個值,打印出他的姓名和年齡,若是原函數這樣調用,使用上面的裝飾器確定會出錯,那就須要在裝飾器函數中作以下修改:def wapper(my_name,my_age)func(my_name,my_age)這兩行加入相同的參數,也是能夠的,但若是name函數我再修改呢,不傳入兩個參數了 ,傳3個 或更多,每次都要去修改豈不是很麻煩,這就須要作一些改變了 。

def timmer(func):      
    def wapper(*args,**kwargs):         #這裏用*args,**kwargs代替,這樣 ,無論原函數傳入多少個參數,均可以匹配
        start_time=time.time()
        func(*args,**kwargs)             #一樣,接受任意多個參數 (若是不懂這個什麼意思,翻看我前面函數篇的博客,有講到)
        stop_time=time.time()
        print("name()函數一共運行了%s 秒"%(stop_time-start_time))
    return wapper

  • 裝飾器添加返回值
      參數問題解決了,下面個人原函數又變了
def name(my_name,my_age):
    time.sleep(2)
    print("my name is %s,my age is %s"%(my_name,my_age))
    return "尼古拉斯  你真年輕"
print(name("尼古拉斯趙四",18))
運行結果:
my name is 尼古拉斯趙四,my age is 18
尼古拉斯  你真年輕

  在這個函數中,我需求是在運行完函數返回"尼古拉斯 你真年輕"這句話,仍是用上面的裝飾器返回值會是my name is 尼古拉斯趙四,my age is 18 name()函數一共運行了2.000795364379883 秒 None,返回值是None而不是想要的結果,你們能夠試一下,因此要加以下修改:

def timmer(func):
    def wapper(*args,**kwargs):         
        start_time=time.time()
        res=func(*args,**kwargs)            #將func()運行結果賦值給變量res
        stop_time=time.time()
        print("name()函數一共運行了%s 秒"%(stop_time-start_time))
        return res                          #返回res,其實就是在返回name()
    return wapper

  上面就詳細寫了裝飾器的實現過程,就先寫到這,後續我會豐富裝飾器並作出一個通俗易懂的例子供你們參考,稍後一些時間也會發布在個人博客裏,感興趣的到時候能夠看一下,但願能夠幫助你們對裝飾器有更好的理解。

相關文章
相關標籤/搜索