亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

python基礎知識學習——裝飾器

系統 1794 0

** 裝飾器**

1.裝飾器的概念

  • 裝飾器的本質就是一個函數,它的作用是為其他函數添加一個新的功能,但是不改變原函數的源代碼和調用方式。
  • 裝飾器的兩大原則:
    1. 不修改被修飾函數的源代碼
    2. 不修改被修飾函數的調用方式

2.裝飾器的知識儲備(或者我們可以理解成,一個裝飾器是由什么組成)

  • 裝飾器 = 高階函數+函數嵌套+閉包

3.裝飾器的實現

  1. 首先我們定義一個累加求和的函數
            
              import time
def cal():
    res = 0
    for i in range(100):
        res+=i
    time.sleep(1)
    print("函數的運行結果為",res)
    return res
cal()

            
          

在這里插入圖片描述

  • 這是我們的一個初始函數,如果此時我們需要為這個函數添加一個功能,例如我們需要計算這個函數需要的時間。想想好像也容易做到這一點的吧。通過調用函數前和函數運行結束后分別記下時間戳,那么我們就成功的為這個函數記下了運行的時間。
            
              import time
def cal():
    start_time = time.time()
    res = 0
    for i in range(100):
        res += i
    time.sleep(1)
    stop_time = time.time()
    print("函數的運行結果為", res)
    print("函數的運行時間為",stop_time-start_time)
    return res
cal()

            
          

這是修改函數運行后得到的結果

  • 通過上面的這種方法,我們很容易就達到了我們需要做得目的了,也就是說我們成功的為函數添加了一個新的功能——記錄函數運行的時間。不過,這只是其中的一個函數,如果說有成百上千個函數,我們也要這樣一個個的添加功能嗎?這顯然不太現實。
  • 更關鍵的一點,我們這種做法已經違反了開放封閉原則。開放封閉原則簡單的理解就是我們寫的程序上線以后,我們就不能對程序的源代碼經行修改。如果函數在其他位置被調用了的話,我們便不知道會不會出現其他的后果,有可能引起一系列的連鎖反應。所以說,上面的方法,我們是行不通的。
  • 裝飾器的第二個原則: 不修改被修飾函數的調用方式 ,我可以為函數添加新的功能,但是不能改變它的調用方式,在上面的函數中,我們通過cal()進行調用函數,那么我們添加了功能之后,我們就必須按照原來的方法,用cal()來調用函數。
  1. 高階函數的使用
    • 首先我們說一下,什么是高階函數?
      滿足下面兩個條件中的其中一個都可以成為高階函數:
      • 函數接受的參數是一個函數名
      • 函數的返回值是一個變量名
    • 我們繼續拿上面的函數做例子
            
              import time
def cal():
    res = 0
    for i in range(100):
        res+=i
    time.sleep(1)
    print("函數的運行結果為",res)
    return res

def test(func):
    print(func)
    func()
test(cal)

            
          

那么這里的test就是一個高階函數。因為它接收的參數是一個函數名func,看到這里,我們就可以進行對函數cal添加新的功能了,繼續以記錄函數運行時間作為一個例子。

            
              import time
def cal():
    res = 0
    for i in range(100):
        res+=i
    time.sleep(1)
    print("函數的運行結果為",res)
    return res

def test(func):
    print(func)
    start_time = time.time()
    func()
    stop_time =  time.time()
    print("函數運行的時間為",stop_time-start_time )

test(cal)

            
          

來看下這個函數運行的結果:
python基礎知識學習——裝飾器_第1張圖片
那這時候,我們不但沒有修改原函數的源代碼,而且還為函數添加了一個新的功能,我們的功能就實現了,裝飾器就這么結束了。難道真的結束了嗎?你注意到了沒,函數的方式已經發生改變了,初始我們調用函數使用cal(),而現在卻是test(cal),這就違反了我們上述所說的裝飾器的第二原則。
那么我們來看下高階函數第二種定義:返回值是一個函數名

            
              import time
def cal():
    res = 0
    for i in range(100):
        res+=i
    time.sleep(1)
    print("函數的運行結果為",res)
    return res
    
def test(func):
    start_time = time.time()
    cal()
    stop_time = time.time()
    print("函數的運行時間為",stop_time-start_time)
    return func
    
cal = test(cal)
# print(res)
cal()

            
          

通過傳入參數,能夠讓我們避免對源代碼的修改,但修改了函數的調用方式
通過返回值為函數名,能夠讓我們避免修改函數的調用方式
這時我們通過對添加功能函數的參數傳入為函數,返回值為函數名,此時,我們滿足了裝飾器的兩個原則。我們這種貌似是可取的,其實不然。我們可以發現在添加功能時,需要對函數執行一次才可以,這不符合我們的要求。
因此,僅僅只有高階函數并不能滿足于對我們的需求,不知道還是否記得開頭寫的對裝飾器的知識儲備。裝飾器=高階函數+函數嵌套+閉包。接下來,我們來了解什么時閉包吧。

4.閉包

  1. 閉包的理解 :有很多小伙伴都不懂什么是什么,比較模糊,其實我們可以理解閉包為一種特殊的嵌套函數,這個函數由兩個函數嵌套組成,那么在結構上就有外函數和內函數的說法。 外函數返回值是內函數的引用結果
  • (1)外層函數out_func可以調用內層函數in_func,但無法引用in_func內部的變量y

  • (2)內層函數in_func可以引用外層函數out_func的變量x

            
              def out_func():
	x = 1
    def in_func(y):
        return x+y
    return in_func
test = out_func()
print(test(10))

            
          
  • test是閉包函數,自由變量是x,因為它不是in_func這個函數內部的變量,但是卻被in_func引用。test的結果是函數in_func的地址,那么通過test()便可調用函數in_test.
  • 每一層的函數,我們可以成為一個包,而閉就是封裝的意思,它封裝的是變量,嵌套在函數內的函數等價于一個變量,遵循函數即變量的原則。其實閉包有點類似于作用域,若最里層需要一個變量,我們就可以在當前層定義,如果當前層不允許,則往上一層,一層一層的往外,也就是說我們可以在最外層定義一個變量,那就可以滲透到最里層
  • 閉包的最大用處也是用于裝飾器,接下來,讓我們看看裝飾器的作用吧。

5.裝飾器的實現

  • 我們繼續以上面的例子來了解裝飾器,我們要為一個函數計算運行時間。
    來,先看一段代碼吧。
            
              import time
def timer(func):
    def wrapper():
        # print(func)
        start_time = time.time()
        func()
        stop_time = time.time()
        print(stop_time-start_time)
    return wrapper
#@timer  #等價于 cal = timer(cal)
def cal():
    res = 0
    for i in range(100):
        res+=i
    time.sleep(1)
    print("函數的運行結果為",res)
    return res

cal = timer(cal)
cal()

            
          

事到如此,我們基本 完成了裝飾器的功能了,我們不但沒有修改函數的源代碼,也沒有修改其調用方式,還為它添加了一個新的功能。
不過,這樣子還存在這一丟丟瑕疵,因為每次給函數添加功能是都需要做一次賦值操作,那能不能不每次調用都賦值一次? 那就要用到了一個 @,這是python提供的一個功能。
@timer 相當于 cal = timer(cal),我們要修飾那個函數,就在那個函數前添加@timer

6.含返回值的裝飾器

  • 上面的樣子看上去似乎已經滿足了我們的要求了,但好像還有點小問題額。。。如果我們需要用到cal函數的返回結果的時候,這貌似不符合我們的心意啊。
            
              res = cal()
print("這是函數的返回結果的cal:",res)

            
          

輸出的結果竟然是None?????
python基礎知識學習——裝飾器_第2張圖片

  • 仔細研究一下,cal經過裝飾后,本質上就是上面的經過升級后的wrapper函數,而這個函數是沒有返回值的,因此,cal()運行的結果返回就是默認的None啦。
            
                  def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print(stop_time-start_time)

            
          
  • 來看看這一段wrapper函數,我們需要func的返回值,那么就需要在wrapper函數加上return返回咯,返回的值是func的結果,那就簡單了,吧func返回的結果賦值給變量res,然后return返回res這樣就解決啦。
            
              import time
def timer(func):
    def wrapper():
        # print(func)
        start_time = time.time()
        res = func()
        stop_time = time.time()
        print(stop_time-start_time)
        return res
    return wrapper
@timer 
def cal():
    res = 0
    for i in range(100):
        res+=i
    time.sleep(1)
    print("函數的運行結果為",res)
    return res

res = cal()
print("這是函數的返回結果的cal:",res)

            
          

7.含參數的裝飾器

上面的例子,我們的cal函數都是不帶參數的,但是,我們并不能保證以后我們所寫的每一個函數都是不帶參數的。

  • 舉個例子吧,我們在原來的基礎上定義了一個test1函數:
            
              def timer(func):
    def wrapper():
        # print(func)
        start_time = time.time()
        res = func()
        stop_time = time.time()
        return res
    return wrapper
    
@timer
def cal():
    res = 0
    for i in range(100):
        res+=i
    time.sleep(1)
    print("函數的運行結果為",res)
    return res
@timer
def test1(name):
    time.sleep(1)
    print("這是%s"%(name))
    return res

            
          

我們在不修改裝飾器的情況下,就會報錯了,wrapped函數不需要函數,但是一個給出了。
在這里插入圖片描述
那么我們就要在wrapped函數中加上參數name,不過,依然不能解決問題。
在這里插入圖片描述
這是我們就需要用到了可變參數了。 如果我們不確定要往函數中傳入多少個參數,或者我們想往函數中以列表和元組的形式傳參數時,那就使要用*args; 如果我們不知道要往函數中傳入多少個關鍵詞參數,或者想傳入字典的值作為關鍵詞參數時,那就要使用**kwargs。

            
              def timer(func):
    def wrapper(*args,**kwargs):
        # print(func)
        start_time = time.time()
        res = func(*args,**kwargs)
        stop_time = time.time()
        return res
    return wrapper

            
          

這里我們對wrapper函數進行修改,在wrapper中就如位置可變參數和關鍵字可變參數,這樣的話,我們就不用再擔心被修飾函數有多少個參數,以及我們定義的裝飾器可以修飾任何函數啦。

好啦,裝飾器就到此結束了。。。。。。

更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 9999热视频 | 精品在线视频观看 | 精品国产精品久久一区免费式 | 97影院理论午夜论不卡 | 久久久不卡国产精品一区二区 | 日本一级片免费观看 | 夜夜操网站 | 欧美洲精品亚洲精品中文字幕 | 亚洲社区在线观看 | 成人精品视频网站 | 亚洲欧美日韩在线精品2021 | 色视频网| 国产成人精品久久综合 | 国产日韩网站 | 精品国产网 | 91欧美亚洲| 理论片在线观看视频 | 欧美乱人免费视频观看 | 国产精品美女免费视频大全 | 亚洲欧美综合视频 | 国产精品一级视频 | 免费成人高清视频 | 天天狠狠色噜噜 | 色综合欧美 | 国产成人精品第一区二区 | 亚洲午夜视频在线 | 天天干夜夜怕 | 亚洲精品久久久久久下一站 | 日本一区二区三区高清在线观看 | 成人在激情在线视频 | 久草免费公开视频 | 韩国色三级伦不卡高清在线观看 | 久久精品综合国产二区 | 日本特级黄毛片毛片视频 | 久久宗合色 | 亚洲国产精品网站久久 | 久久狠狠色狠狠色综合 | 久热国产精品 | 四虎免费影院4hu永久免费 | 私人小影院在线 观看 | 深夜啪啪网站 |