<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當我們在外部函數中定義了一個內部函數,並且內部函數能夠讀取到外部函數內的變數,這種函數我們就稱為閉包。簡單來說,閉包就是能夠讀取外部函數內的變數的函數。
閉包的架子大概是這樣:
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)
上面程式碼執行結果如下:
x的值:12, y的值:34, x + y 的值:46
上面的閉包程式碼,和我們之前學習的裝飾器類似,我們在外部函數 demo_outer
下定義了一個內部函數 demo_inner
,並且外部函數的返回值就是內部函數,同時在內部函數中,我們參照到了外部函數的變數 x ,而閉包的作用就是可以將外層函數的變數儲存在記憶體中而不被銷燬。
我們先準備一個函數add(),每次呼叫該函數都只能傳一個數 num ,每次返回的結果都是基於上一次結果的值 sum 進行累加操作。例如,sum的預設值為0,如果我們依次呼叫 add(10)、add(20)、add(30) 後,期望得到的最終結果是 sum = 60。
對於該問題,因為需要在函數內部對函數外部的變數進行處理,我們可能會考慮使用 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
上面程式碼中,我們在函數中通過全域性變數 global 將 sum 宣告為全域性變數,最終返回的結果也符合我們的期望。但因為全域性變數太靈活了,不同模組函數都能自由存取到全域性變數,所以一般不推薦在函數內部中定義全域性變數。
對於上面的問題,除了使用全域性變數外,我們還可以通過 閉包 來實現。
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
在上面的閉包函數中,定義了外層函數和內層巢狀函數,執行過程中,呼叫外層函數 get_add_sum 返回的就是內層巢狀函數 add_num ,因為Python中函數也是物件,所以可以直接返回 add_num 。
而在內層巢狀函數中則實現了我們想要的累加操作,在這裡我們需使用關鍵字 nonlocal 來宣告外層函數中的變數,否則無法對外層函數中的變數進行修改,其作用域只在閉包函數裡面,所以當我們在最後列印 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)
上面程式碼的執行結果如下:
外部的i值:0
外部的i值:1
外部的i值:2
內部的i值:2
內部的i值:2
內部的i值:2
[12, 12, 12]
可以看到 res 的結果並不是 [10, 11, 12] ,因為 i 是外層函數的變數,並且其值是按 0,1,2 變化,因為該函數中無法儲存外層函數的變數,故對於內部函數,其實際只能存取到最後外部變數的值 2 ,導致最終結果為:[12, 12, 12]。
在這裡,我們可以使用閉包來儲存函數的外部變數,修改程式碼如下:
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)
上面程式碼的執行結果如下:
外部的i值:0
外部的i值:1
外部的i值:2
內部的i值:0
內部的i值:1
內部的i值:2
[10, 11, 12]
在Python中,閉包傳遞的引數是變數,裝飾器傳遞的引數是函數物件,它們只是在傳參內容上有不同。那麼裝飾器是不是屬於閉包的一種呢,我們要怎麼判斷一個函數是否是閉包呢?
我們可以列印 閉包 和 裝飾器 的屬性 __closure__
,如果一個函數是閉包,那麼檢視該屬性將會返回一個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__))
執行結果如下:
閉包的屬性:(<cell at 0x0000023F48C3C8E8: int object at 0x00007FF92017D4A0>,)
裝飾器的屬性:(<cell at 0x0000023F48C3C8B8: function object at 0x0000023F48CB97B8>,)
所以結合上面的結果,我們也可以這樣理解:裝飾器本質上就是一個閉包函數,它只是一個傳遞函數物件的閉包函數。
到此這篇關於Python中的閉包使用及作用的文章就介紹到這了,更多相關Python閉包內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45