python中裝飾器之真面目一

寫代碼要遵循開發封閉原則,規定已經實現的功能代碼不允許被修改,但可以被擴展,通俗來說,不要瞎改改~~

1,關於函數的說明兩點

(1):先說一下函數,到底什麼是函數:函數即變量,函數存在內存裏面,函數名就像門牌號碼(內存地址),尋找對應的函數(變量),這裏變量test根據內存地址,找到函數test(),變量x根據內存地址找到變量3

(2):所以函數par_b跟par_b()是不同的概念,先看運行結果,可以看到par_b()返回的是函數的運行結果,而par_b返回的內存地址

def par_b(a):
    '''返回傳入的參數值a '''
    return a

print(par_b(2)) # 表示執行函數
print(par_b) #表示是函數,返回的是函數的對象,即函數的內存地址
'''
運行返回值:
2   
<function par_b at 0x00000157BB3AC268>
'''

2,裝飾器:裝飾器本身就是函數,用函數去裝飾另外一個函數,即爲其他函數附加功能

裝飾器不能違背的兩個規則:

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

3,裝飾器=高階函數+嵌套函數

  3.1:高階函數:滿足下面其一均成爲高階函數

(1)把一個函數名作爲實參傳給另外一個函數,即把函數當成另外一個函數的參數

(2)返回值中包含函數名,即:一個函數的返回值爲另外一個函數(若返回值爲該函數本身,則爲遞歸)

第一種情況:

原來一個支付功能函數,現在領導說要統計支付函數花費的時間,這時候不好修改原函數功能的,因爲你不知道有多少業務在使用這個函數~~

import time
def order():
    '''購買商品'''
    time.sleep(1)
    print('購買函數')

def order_count(func):
    '''購買次數'''
    order_start = time.time()
    func()
    order_stop = time.time()
    print('購買花費的時間爲:',(order_stop-order_start))

order_count(order)
'''
運行結果:
購買函數
購買花費的時間爲: 1.0010454654693604
'''

備註:以上使用了高階函數的條件一:函數作爲參數,但這不是裝飾器,顯然我們違背了裝飾器規則之一:不能改變被修飾函數的調用方式

第二種情況:

import time
def order():
    '''購買商品'''
    time.sleep(1)
    print('購買函數')

def order_count(func):
    '''購買次數'''
    order_start = time.time()
    func()
    order_stop = time.time()
    print('購買花費的時間爲:',(order_stop-order_start))
    return func

order=order_count(order)
order()  #但其實order函數運行了兩次
'''
運行結果:
購買函數
購買花費的時間爲: 1.0009815692901611
購買函數
'''

備註:以上添加了高階函數條件二:返回值中包含函數名,此功能幫我實現了不修改原調用方式的規則

  3.2:一看裝飾器:所以結合高階函數的定義,我們來看裝飾器:

(1) 把一個函數名當做實參傳給另一個函數(不修改被裝飾函數的情況下不修改源代碼 )

(2) 返回值中包含函數名(不修改函數的調用方式  )

  3.3:嵌套函數: 函數套函數,外套裏面再穿一個外套,爲啥這樣穿,冷啊

x = ''
def grandba():
    x = '爺爺'
    print('第一層返回值:',x)
    def dad():
        x = '爸爸'
        print('第二層返回值:', x)
        def son():
            x = '兒子'
            print('第三層返回值:', x)
        son()
    dad()
grandba()
'''
執行結果:
第一層返回值: 爺爺
第二層返回值: 爸爸
第三層返回值: 兒子
'''

備註:說一下上面的執行步驟,第一步:讀到def grandba():時,首先會把該函數存儲到內存中,不會執行該函數內部邏輯,第二步:讀到grandba(),纔會根據函數名到內存中找到該函數,然後執行,此時纔會執行該函數內部的邏輯代碼,後面一樣的道理

  3.4:再看裝飾器:在被修飾函數的前面,用@符號調用

4,帶參數裝飾器

題外話:

之前寫的時候函數w2忘記寫return w2,然後報錯:TypeError: 'NoneType' object is not callable,研究一下原因:

以上運行結果爲:

<function order_count.<locals>.w2 at 0x0000029B13E80840>
購買的商品爲: apple iphone
購買花費的時間爲: 4.394911289215088

運行時打印了w2,輸出的內存地址 程序運行到order("apple iphone")內存地址一模一樣,流淚啊,恍然大悟了吧,其實到這一步,有一種移花接木的感覺,體會到那句話了,在不該改變原有的調用方式的基礎上,給原函數添加附加功能,到這個一步就是:你就我,我就是你啊~~~

另外看到說的很好的:https://www.cnblogs.com/wupeiqi/articles/4980620.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章