python閉包和裝飾器

一、python函數作用域LEGB

python解釋器查找變量的原則(順序):
L→E→G→B
L:Local函數內部作用域
E:enclosing函數內部與內嵌函數之間
G:gobal全局作用域
B:build-in內置作用域

example:

value1 = 5
def my_func():
    value2 = 6
    print(id(value2))
    def in_func():
        a = max(value1, value2)
        print(a)
    return in_func

上面示例代碼中max函數為python的內建函數,在in_func函數中,max這個變量python解釋器首先會在in_func這個函數中查找,即local函數內部作用域中查找,然后按順序再到E-G-B查找,最后在build-in內置作用域查找到。

二、閉包

1、概念:內部函數中對enclosing作用域的變量進行引用

上述例子中my_func函數中的內部函數in_func引用了enclosing作用域中的value2變量,這就叫做閉包。
由于函數執行完后變量會被回收,當執行完my_func這個函數后,value2變量會被回收。那么如果我們再次調用in_func這個函數時,需用引用到value2這個變量,是否會報錯?
看以下代碼:

example:

value1 = 5
def my_func():
    value2 = 6
    print(id(value2)) #打印value2的ID值
    def in_func():
        a = max(value1, value2)
        print(a)
    return in_func

f = my_func()    #my_func返回的是in_func,此時f指向in_func函數
f()    #f()相當于in_func(),調用了infunc函數
print(f.__closure__)

ouput:

501351024
6
(<cell at 0x0000000000AF8D98: int object at 0x000000001DE20270>,)

如果內部函數引用了enclosing作用域的變量,會將變量添加到函數__closure__的屬性中去。當再次查找這個變量時,會直接去函數__closure__的屬性中查找。我們可以看到代碼的輸出結果第一行即為value2的內存地址(501351024轉換為16進制:1DE20270)和__closure__屬性中的int對象的地址是一樣的( int object at 0x000000001DE20270)。

2、那么閉包到底有什么用呢?

我們來看下以下兩段代碼:

(1)、

def func_100(value):
    passline = 60
    if value >= passline:
        print('pass')
    else:
        print('failed')

def func_150(value):
    passline = 90
    if value >= passline:
        print('pass')
    else:
        print('failed')
func100(59)
func150(89)

(2)、

def set_passline(passline):
    def in_func(value):
        if value >= passline:
            print('pass')
        else:
            print('failed')
    return in_func
f_100 = set_passline(60) #passline=60被存儲在f_100的__closure__屬性中
f_150 = set_passline(90) #passline=90被存儲在f_150的__closure__屬性中
f_100(59)
f_150(89)

兩段代碼都能正確判斷滿分是100或150時,分數是否及格。但是第二段代碼,由于運用閉包,代碼復用性更高。

3、閉包更高級的應用

將閉包的概念中的變量變成函數,同樣適用。即:內部函數中對enclosing作用域的函數進行引
example:

def my_sum(*args):
    return(sum(args))

def my_average(*args):
    return sum(args)/len(args)

def dec(func):
    def in_dec(*args):
        if len(args) == 0:    #對參數進行判斷,如果沒有參數直接返回0
            return 0
        for i in args:
            if not isinstance(i, int):    #對參數進行判斷,如果有一個參數不是int類型,直接返回0
                return 0
        return func(*args)    #此處對enclosing作用域的func函數進行引用
    return in_dec

evo_my_sum = dec(my_sum)
evo_my_average = dec(my_average)

PS:此處求和函數(my_sum)和求平均值函數(my_average)只對參數是否int類型進行判斷。
我們來分析下代碼:evo_my_sum = dec(my_sum)
由于函數dec返回的是in_dec函數
所以evo_my_sum指向的是in_dec函數
=》evo_my_sum = in_dec
=》evo_my_sum(1, 2, 3) = in_dec(1, 2, 3)
那么in_dec(1, 2, 3)evo_my_sum(1, 2, 3)會對求和的參數先進行判斷后,再調用my_sum函數。
同樣道理:
evo_my_average會對求平均的參數先進行判斷后,再調用my_average函數。
這樣就可以對參數統一進行判斷后再各自調用不同的函數。

三、裝飾器

裝飾器是用來裝飾函數的,它返回一個函數對象。語法:@Decorator
現在我們已經定義了一個my_sum函數

def my_sum(*args):
    return(sum(args))

假設我們要增加my_sum函數的功能,比如,在函數調用前對參數進行一個判斷,但又不希望修改my_sum函數的定義,這種在代碼運行期間動態增加功能的方式,稱之為“裝飾器”(Decorator)。
我們要定義一個能判斷參數的decorator,如下:

def dec(func):
    def in_dec(*args):
        if len(args) == 0:
            return 0
        for i in args:
            if not isinstance(i, int):
                return 0
        return func(*args)
    return in_dec

按照python裝飾器的語法:

@dec
def my_sum(*args):
    return(sum(args))

這樣,調用my_sum函數,不僅會運行my_sum函數本身,還會在運行my_sum函數前,對參數進行判斷。
把@dec放到my_sum函數的定義處,相當于執行了語句:
my_sum = dec(my_sum)
看到這里是否覺得有點熟悉?
其實這個語句和上述閉包的高級應用是一樣的,只不過那部分把my_sum改成了evo_my_sum
裝飾器到這里還差最后一步:
my_sum函數經過裝飾后,由于dec(my_sum)返回的是in_dec函數,此時my_sum.__name__屬性將從‘my_sum’變成‘in_dec’,為了保證此屬性不變,需在in_dec函數定義前加上語句@functools.wraps(func),保證my_sum.__name__屬性不變。否則,有些依賴函數簽名的代碼執行就會出錯。一個完整的decorator的寫法如下:

import functools

def dec(func):
    @functools.wraps(func)
    def in_dec(*args):
        if len(args) == 0:
            return 0
        for i in args:
            if not isinstance(i, int):
                return 0
        return func(*args)
    return in_dec

decorator可以增強函數的功能,定義起來雖然有點復雜,但使用起來非常靈活和方便。
以上是觀看慕課網《python裝飾器》以及廖雪峰教程python裝飾器的總結。

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

推薦閱讀更多精彩內容

  • 閉包: 在函數內部在定義一個函數,并且這個函數用到了外面函數的變量,這個函數和用到的變量,稱為閉包. deftes...
    界面大叔閱讀 224評論 0 0
  • 前幾天學習python裝飾器時,看各種例子,上來就是一個嵌套函數,還返回一個函數對象(返回內嵌函數),學得我是一臉...
    喵小琪閱讀 356評論 0 1
  • 本文為《爬著學Python》系列第四篇文章。從本篇開始,本專欄在順序更新的基礎上,會有不規則的更新。 在Pytho...
    SyPy閱讀 2,509評論 4 11
  • 曾經,在酷暑的夏天,寒冷的冬季,每天早上出門等公交車的時候我就會想要是有輛自行車就好了。這樣我就可以騎車在家和地鐵...
    葉子綠漫天閱讀 211評論 0 0