9. 裝飾器和閉包

[TOC]

裝飾器

閉包函數

思考練習

def addx(x):
    def adder(y):return x+y
    return adder
c = addx(8)
print(c(10))

要計算這段代碼的結果,首先需要具備“python中一切皆對象”的思想。在python中,函數也是一樣可以被賦值傳參加入運算的。這就成為閉包函數實現前提,因為閉包的梗概(我理解)就在于:定義一個函數,并且返回值為子函數方法。
上述代碼中addx(x)是一個需要傳參x進入的函數,并且有一個子函數adder(y)同樣需要被傳參y。addx的返回值就是adder方法
我們都知道函數名稱后面跟‘()’就是運行函數,函數通常會運算并且得出一個值,當你暫時不需要這個值的時候,你可以先return func,而不添加(),這樣做的意義就在于,保留返回了這個函數運算的方法,在需要傳參進入時候再進行傳參。

當然也可以直接輸出這個func,這樣的結果就是這個函數在內存的存儲位置。

上述代碼中,c = addx(8)完成了addx中對x的傳參,傳入x之后adder(y)返回的語句就相當于8+y,并且adder返回給addx的方法,仍然是adder(y)

所以分解輸出式子:
c(10)==addx(8)(10)==【adder(10)且已知x = 8】==8+10==18

為什么要使用裝飾器

python中裝飾器的多種用途:
輸出日志、參數檢查、代理設置、計數計時、結果緩存等。

裝飾器可以在不影響原代碼的前提下,為原代碼輸出結果附加一些內容

開放封閉原則:軟件實體應該對擴展開放,而對修改封閉。開放封閉原則是所有面向對象原則的核心。軟件設計本身所追求的目標就是封裝變化,降低耦合,而開放封閉原則正是對這一目標的最直接體現。
開放封閉原則主要體現在兩個方面:
對擴展開放,意味著有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。
對修改封閉,意味著類一旦設計完成,就可以獨立其工作,而不要對類盡任何修改。

裝飾器范例代碼:

(一)老師教的定義方式

import time     #載入time函數庫

def timer(func):        #定義裝飾器名稱
    def wrapper():      #定義用于返回閉包的內建函數
        start_time = time.time()
        func()
        end_time = time.time()
        print('func run time is %s'%(end_time-start_time))
    return wrapper      #返回閉包函數的方法,后面添加括號的話,就執行這個方法并且獲得結果

@timer          #語法糖,注意這個@語法要在‘def’前面使用
def index():
    time.sleep(1)
    print('this is a test func')

index()

(二)網上的花活

https://zhuanlan.zhihu.com/p/23510985

# 構建裝飾器
def logging(func):
    @functools.wraps(func)
    def decorator():
        print("%s called" % func.__name__)
        result = func()
        print("%s end" % func.__name__)
        return result
    return decorator

# 使用裝飾器
@logging
def test01():
    return 1

# 測試用例
print(test01())
print(test01.__name__)

上面這個functools在IDE中有報錯,可能是一個外部調用方法,所以這種方法暫時不管,下次看到的時候,只要知道這是在定義一個裝飾器就可以了。

考慮到裝飾器需要適配多種參數的函數,提高復用率。
所以需要這樣定義裝飾器:

import time
def timer(func):
    def wrapper(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)
        end_time = time.time()
        print('======')
    return wrapper

結合閉包的概念,梳理一下裝飾器運行的邏輯:

定義一個裝飾器名稱函數
定義一個裝飾器wrapper,可以提供多種參數的傳參
裝飾器功能,
裝飾目標函數,和wrapper相同的傳參條件
裝飾器效果(可有可無)
返回wrapper不加括號

我所理解的不加括號的意義,就是暫時不調用這個閉包內的函數,直到在外部調用時,手動添加上'()',就執行這個函數的功能。這個方法剛好和return func()相反,因為添加括號后,返回的就是一個結果值。而不添加,就是一種運算。

有參裝飾器

def auth():
    def wrapper(*args,**kwargs):
        name = input('username:')
        password = input('password:')
        if name =='scott' and password =='123':
            print('auth successful')
            res = func(*args,**kwargs)
            return res
        else:
            print('auth error')
    return wrapper


@auth      #index = auth(index)
def index():
    print('welcome to index page')

思考:

有一個運行時間計數函數裝飾器
有一個用戶登錄認證裝飾器
能否實現“當運行時間計數超過一定時間后,提示用戶登錄認證失敗”
理清裝飾器運行的包裹層關系:

@time
@auth_check
def func()

func() = time(auth_check(func))

但是如果需要校驗函數的運行時間是否超時,就應該把time放在里面

@auth_check
@time
def func()

雖然這樣看起來遞進關系對了,但是裝飾器能否向被定義的函數進行傳參,或者裝飾器之間能否進行傳參?
老師回答:裝飾器并不能向裝飾的函數進行傳參或者運算
如果需要一個對函數運行時間進行判斷并且輸出結果的裝飾器:

import time
def timer(func):
    def wrapper():
        start_time = time.time()
        func()
        end_time = time.time()
        res = end_time-start_time
        if res<= 60:
            print('函數在時限內運行完成')
        else:
            print('函數運行超時')
        print('func run %s'%(end_time-start_time))
    return wrapper

@timer
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容