Python中的閉包

今天我們來學習下Python中的閉包。

什么是閉包

當我們在外部函數(shù)中定義了一個內部函數(shù)虎谢,并且內部函數(shù)能夠讀取到外部函數(shù)內的變量氏涩,這種函數(shù)我們就稱為閉包蚁鳖。簡單來說磺芭,閉包就是能夠讀取外部函數(shù)內的變量的函數(shù)。閉包的架子大概是這樣:

def demo_outer(x):
    def demo_inner(y):
        print("x的值:{}, y的值:{}, x + y 的值:{}".format(x, y, x + y))
    return demo_inner


do = demo_outer(12)
do(34)

上面代碼執(zhí)行結果如下:

x的值:12, y的值:34, x + y 的值:46

上面的閉包代碼才睹,和我們之前學習的裝飾器類似徘跪,我們在外部函數(shù) demo_outer 下定義了一個內部函數(shù) demo_inner 甘邀,并且外部函數(shù)的返回值就是內部函數(shù)琅攘,同時在內部函數(shù)中,我們引用到了外部函數(shù)的變量 x 松邪,而閉包的作用就是可以將外層函數(shù)的變量保存在內存中而不被銷毀坞琴。

閉包的實例

我們先準備一個函數(shù)add(),每次調用該函數(shù)都只能傳一個數(shù) num 逗抑,每次返回的結果都是基于上一次結果的值 sum 進行累加操作剧辐。例如,sum的默認值為0邮府,如果我們依次調用 add(10)荧关、add(20)、add(30) 后褂傀,期望得到的最終結果是 sum = 60忍啤。

對于該問題,因為需要在函數(shù)內部對函數(shù)外部的變量進行處理仙辟,我們可能會考慮使用 global 來處理同波。

sum = 0


def get_add_sum(num):
    global sum
    sum += num
    return sum


print(get_add_sum(10))  # 輸出:10
print(get_add_sum(20))  # 輸出:30
print(get_add_sum(30))  # 輸出:60

print(sum)  # 輸出:60

上面代碼中鳄梅,我們在函數(shù)中通過全局變量 global 將 sum 聲明為全局變量,最終返回的結果也符合我們的期望未檩。但因為全局變量太靈活了戴尸,不同模塊函數(shù)都能自由訪問到全局變量,所以一般不推薦在函數(shù)內部中定義全局變量冤狡。

對于上面的問題孙蒙,除了使用全局變量外,我們還可以通過 閉包 來實現(xiàn)筒溃。

sum = 0


def get_add_sum(sum):
    def add_num(num):
        nonlocal sum
        sum += num
        return sum
    return add_num


add = get_add_sum(sum)
print(add(10))  # 輸出:10
print(add(20))  # 輸出:30
print(add(30))  # 輸出:60

print(sum)  # 輸出:0

在上面的閉包函數(shù)中马篮,定義了外層函數(shù)和內層嵌套函數(shù),執(zhí)行過程中怜奖,調用外層函數(shù) get_add_sum 返回的就是內層嵌套函數(shù) add_num 浑测,因為Python中函數(shù)也是對象,所以可以直接返回 add_num 歪玲。

而在內層嵌套函數(shù)中則實現(xiàn)了我們想要的累加操作迁央,在這里我們需使用關鍵字 nonlocal 來聲明外層函數(shù)中的變量,否則無法對外層函數(shù)中的變量進行修改滥崩,其作用域只在閉包函數(shù)里面岖圈,所以當我們在最后打印 sum 最終結果,輸出的仍然是其初始值 0 钙皮。

我們再來看一個例子:

def outer():
    res = []
    for i in range(3):
        print("外部的i值:{}".format(i))
        def inner(x):
            print("內部的i值:{}".format(i))
            return i + x
        res.append(inner)
    return res


temp = outer()
res = [i(10) for i in temp]
print(res)

上面代碼的執(zhí)行結果如下:

外部的i值:0
外部的i值:1
外部的i值:2
內部的i值:2
內部的i值:2
內部的i值:2
[12, 12, 12]

可以看到 res 的結果并不是 [10, 11, 12] 蜂科,因為 i 是外層函數(shù)的變量,并且其值是按 0短条,1导匣,2 變化,因為該函數(shù)中無法保存外層函數(shù)的變量茸时,故對于內部函數(shù)贡定,其實際只能訪問到最后外部變量的值 2 ,導致最終結果為:[12, 12, 12]可都。

在這里缓待,我們可以使用閉包來保存函數(shù)的外部變量,修改代碼如下:

def outer(i):
    print("外部的i值:{}".format(i))
    def inner(x):
        print("內部的i值:{}".format(i))
        return i + x
    return inner


def demo():
    res = []
    for i in range(3):
        res.append(outer(i))
    return res


temp = demo()
res = [i(10) for i in temp]
print(res)

上面代碼的執(zhí)行結果如下:

外部的i值:0
外部的i值:1
外部的i值:2
內部的i值:0
內部的i值:1
內部的i值:2
[10, 11, 12]

閉包和裝飾器的區(qū)別

在Python中渠牲,閉包傳遞的參數(shù)是變量旋炒,裝飾器傳遞的參數(shù)是函數(shù)對象,它們只是在傳參內容上有不同签杈。那么裝飾器是不是屬于閉包的一種呢瘫镇,我們要怎么判斷一個函數(shù)是否是閉包呢?

我們可以打印 閉包 和 裝飾器 的屬性 __closure__,如果一個函數(shù)是閉包汇四,那么查看該屬性將會返回一個cell對象組成的tuple對象接奈。

def demo_outer(x):
    """
    閉包
    """
    def demo_inner(y):
        print("x的值:{}, y的值:{}, x + y 的值:{}".format(x, y, x + y))
    return demo_inner


def demo_decorator(func):
    """
    裝飾器
    """
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper


@demo_decorator
def method():
    pass


do = demo_outer(5)  # 閉包
print("閉包的屬性:{}".format(do.__closure__))

dd = demo_decorator(method)  # 裝飾器
print("裝飾器的屬性:{}".format(dd.__closure__))

執(zhí)行結果如下:

閉包的屬性:(<cell at 0x0000023F48C3C8E8: int object at 0x00007FF92017D4A0>,)
裝飾器的屬性:(<cell at 0x0000023F48C3C8B8: function object at 0x0000023F48CB97B8>,)

所以結合上面的結果,我們也可以這樣理解:裝飾器本質上就是一個閉包函數(shù)通孽,它只是一個傳遞函數(shù)對象的閉包函數(shù)序宦。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市背苦,隨后出現(xiàn)的幾起案子互捌,更是在濱河造成了極大的恐慌,老刑警劉巖行剂,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秕噪,死亡現(xiàn)場離奇詭異,居然都是意外死亡厚宰,警方通過查閱死者的電腦和手機腌巾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铲觉,“玉大人澈蝙,你說我怎么就攤上這事∧煊模” “怎么了灯荧?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長盐杂。 經(jīng)常有香客問我逗载,道長,這世上最難降的妖魔是什么链烈? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任厉斟,我火速辦了婚禮,結果婚禮上测垛,老公的妹妹穿的比我還像新娘捏膨。我一直安慰自己秧均,他們只是感情好食侮,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著目胡,像睡著了一般锯七。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上誉己,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天眉尸,我揣著相機與錄音,去河邊找鬼。 笑死噪猾,一個胖子當著我的面吹牛霉祸,可吹牛的內容都是我干的。 我是一名探鬼主播袱蜡,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼丝蹭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坪蚁?” 一聲冷哼從身側響起奔穿,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎敏晤,沒想到半個月后贱田,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡嘴脾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年男摧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片译打。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡彩倚,死狀恐怖,靈堂內的尸體忽然破棺而出扶平,到底是詐尸還是另有隱情帆离,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布结澄,位于F島的核電站哥谷,受9級特大地震影響,放射性物質發(fā)生泄漏麻献。R本人自食惡果不足惜们妥,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望勉吻。 院中可真熱鬧监婶,春花似錦、人聲如沸齿桃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽短纵。三九已至带污,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間香到,已是汗流浹背鱼冀。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工报破, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人千绪。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓充易,卻偏偏與公主長得像,于是被迫代替她去往敵國和親荸型。 傳聞我的和親對象是個殘疾皇子蔽氨,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容

  • 閉包這個概念在很多語言中都有,比如 Java帆疟, JS鹉究,之前的文章中也提到過這個閉包,但是感覺這個概念還是有一點模糊...
    Vector_Wan閱讀 181評論 0 4
  • 本文首發(fā)于:行者AI[https://xingzheai.cn/details/ea6ffefcc0f] pyth...
    行者AI閱讀 251評論 0 1
  • 什么是閉包踪宠?閉包有什么用自赔?為什么要用閉包?今天我們就帶著這3個問題來一步一步認識閉包柳琢。 閉包和函數(shù)緊密聯(lián)系在一起绍妨,...
    碼農小楊閱讀 354評論 0 2
  • 什么是閉包?閉包有什么用柬脸?為什么要用閉包他去?今天我們就帶著這3個問題來一步一步認識閉包。 閉包和函數(shù)緊密聯(lián)系在一起倒堕,...
    _Cappuccino_閱讀 113評論 0 0
  • 本文作者:黎智煊灾测,叩丁狼高級講師。原創(chuàng)文章垦巴,轉載請注明出處媳搪。 什么是閉包?閉包有什么用骤宣?為什么要用閉包秦爆?今天我們就...
    叩丁狼教育閱讀 233評論 0 0