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

python基礎(chǔ)知識學(xué)習(xí)——裝飾器

系統(tǒng) 1596 0

** 裝飾器**

1.裝飾器的概念

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

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

  • 裝飾器 = 高階函數(shù)+函數(shù)嵌套+閉包

3.裝飾器的實現(xiàn)

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

            
          

在這里插入圖片描述

  • 這是我們的一個初始函數(shù),如果此時我們需要為這個函數(shù)添加一個功能,例如我們需要計算這個函數(shù)需要的時間。想想好像也容易做到這一點的吧。通過調(diào)用函數(shù)前和函數(shù)運行結(jié)束后分別記下時間戳,那么我們就成功的為這個函數(shù)記下了運行的時間。
            
              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("函數(shù)的運行結(jié)果為", res)
    print("函數(shù)的運行時間為",stop_time-start_time)
    return res
cal()

            
          

這是修改函數(shù)運行后得到的結(jié)果

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

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

            
          

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

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

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

test(cal)

            
          

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

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

            
          

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

4.閉包

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

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

5.裝飾器的實現(xiàn)

  • 我們繼續(xù)以上面的例子來了解裝飾器,我們要為一個函數(shù)計算運行時間。
    來,先看一段代碼吧。
            
              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("函數(shù)的運行結(jié)果為",res)
    return res

cal = timer(cal)
cal()

            
          

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

6.含返回值的裝飾器

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

            
          

輸出的結(jié)果竟然是None?????
python基礎(chǔ)知識學(xué)習(xí)——裝飾器_第2張圖片

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

            
          
  • 來看看這一段wrapper函數(shù),我們需要func的返回值,那么就需要在wrapper函數(shù)加上return返回咯,返回的值是func的結(jié)果,那就簡單了,吧func返回的結(jié)果賦值給變量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("函數(shù)的運行結(jié)果為",res)
    return res

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

            
          

7.含參數(shù)的裝飾器

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

  • 舉個例子吧,我們在原來的基礎(chǔ)上定義了一個test1函數(shù):
            
              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("函數(shù)的運行結(jié)果為",res)
    return res
@timer
def test1(name):
    time.sleep(1)
    print("這是%s"%(name))
    return res

            
          

我們在不修改裝飾器的情況下,就會報錯了,wrapped函數(shù)不需要函數(shù),但是一個給出了。
在這里插入圖片描述
那么我們就要在wrapped函數(shù)中加上參數(shù)name,不過,依然不能解決問題。
在這里插入圖片描述
這是我們就需要用到了可變參數(shù)了。 如果我們不確定要往函數(shù)中傳入多少個參數(shù),或者我們想往函數(shù)中以列表和元組的形式傳參數(shù)時,那就使要用*args; 如果我們不知道要往函數(shù)中傳入多少個關(guān)鍵詞參數(shù),或者想傳入字典的值作為關(guān)鍵詞參數(shù)時,那就要使用**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函數(shù)進行修改,在wrapper中就如位置可變參數(shù)和關(guān)鍵字可變參數(shù),這樣的話,我們就不用再擔(dān)心被修飾函數(shù)有多少個參數(shù),以及我們定義的裝飾器可以修飾任何函數(shù)啦。

好啦,裝飾器就到此結(jié)束了。。。。。。

更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

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

【本文對您有幫助就好】

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

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 69hdxxxx中国| 天天操天天插天天干 | 久久久精品视频免费观看 | 欧美成人黄色网 | 日韩在线一区二区 | 国产精品午夜在线观看 | 一区二区三区精品国产欧美 | 国产精品欧美一区二区三区不卡 | 日日做日日摸夜夜爽 | 国产精品免费视频能看 | 天天射天天做 | 91精选视频 | 久久综合成人网 | 欧美日韩亚洲精品一区 | 日韩精品麻豆 | 私人小影院在线 观看 | 久久只有精品视频 | 久久精品一 | 国内免费一区二区三区视频 | 亚洲 欧美 视频 | 欧美国产精品亚洲精品第一区 | 国产精品ady9| 欧美成人全部费免网站 | 五月婷婷综合激情 | 亚洲毛片免费视频 | 成人欧美| 欧美曰批人成在线观看 | 亚洲美女视频免费 | 91在线视频在线观看 | 热综合一本伊人久久精品 | 野外一级毛片黑人 | 欧美国产亚洲一区二区三区 | 亚洲精品a | 免费看成人国产一区二区三区 | 最新国产精品亚洲 | 国产成人+亚洲欧洲 | 精品一区二区日本高清 | 成年人黄视频大全 | 九九九九热精品视频 | 狠狠艹| 日韩精品一区二区三区免费观看 |