<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在函數內部再定義一個函數,並且這個函數用到了外部函數的變數(LEGB),最後返回新建函數的函數名索引,那麼將這樣的能夠存取其定義時所在的作用域的函數以及用到的一些變數稱之為閉包。被參照的非全域性變數也稱為自由變數 。這個自由變數儲存在外部函數的唯讀屬性 __closure__ 中,會與內層函數產生一個繫結關係,也就是自由變數將不會在記憶體中輕易消失。如下例所示:
# 計算函數被呼叫的次數 def counter(FIRST=0): -----------------__closure__--------------- |cnt = [FIRST] | # 之所以選列表是因為作用域問題,詳見後文 | | |def add_one(): | | cnt[0] += 1 | | return cnt[0] | ------------------------------------------ return add_one # 每當外部函數被呼叫時,都將重新定義內部的函數,而變數 cnt 的值也可能不同 num5 = counter(5) num10 = counter(10) print(num5()) # 6 print(num5()) # 7 print(num10()) # 11 print(num10()) # 12 # 如果這個函數僅僅是巢狀函數,那麼它的 __closure__ 應該是 None print(num5.__closure__) # (<cell at 0x0163FE30: list object at 0x01514A80>,) print(num5.__closure__[0].cell_contents) # 7 print(num10.__closure__[0].cell_contents) # 12 # 或者通過 __code__.co_freevars 檢視函數中是否有自由變數,如果有自由變數,即為閉包 print(num10.__code__.co_freevars) # ('cnt',)
上面程式碼中的 cnt 變數是一個列表,可變物件,但如果是不可變物件,如:numer、tuple 等呢?
def counter(FIRST=0): cnt = FIRST # number def add_one(): cnt += 1 return cnt return add_one num5 = counter(5) print(num5.__closure__) print(num5.__code__.co_freevars) print(num5()) ---------------------------------------------------------------------------- def counter(FIRST=0): cnt = (FIRST,) # tuple def add_one(): cnt[0] += 1 return cnt[0] return add_one num5 = counter(5) print(num5.__closure__) print(num5.__code__.co_freevars) print(num5())
以上範例輸出結果:
None () Traceback (most recent call last): File "test.py", line, in <module> print(num5()) File "test.py", line, in add_one cnt += 1 UnboundLocalError: local variable 'cnt' referenced before assignment ---------------------------------------------------------------------------- (<cell at 0x0180FE10: tuple object at 0x0173A750>,) ('cnt',) Traceback (most recent call last): File "test.py", line, in <module> print(num5()) File "test.py", line, in add_one cnt[0] += 1 TypeError: 'tuple' object does not support item assignment
可以看出,此時 cnt 不再是自由變數,而是變成了區域性變數,且提示 UnboundLocalError 未繫結區域性錯誤。為什麼不是自由變數了呢?為什麼列表就沒問題呢?
這是因為 Python 中並沒有要求先宣告一個變數才能使用它,Python 直譯器認為:在函數體內,只要對一個變數進行賦值操作,那麼這個變數就是區域性變數。
Python的模組程式碼執行之前,並不會經過預編譯,模組內的函數體程式碼在執行前會經過預編譯,因此不管變數名的繫結發生在作用域的那個位置,都能被編譯器知道。
而 cnt += 1 相當於 cnt = cnt + 1,對 cnt 進行了賦值操作,所以 Python 直譯器認為 cnt 是函數內的區域性變數,但是執行的時候,先執行 cnt+1 時發現:
因為先前已經認定 cnt 為區域性變數了,現在在區域性作用域內找不到 cnt 的值,也不會再到外部作用域找了,就會報錯。所以說現在 cnt 已經不是自由變數了。
那麼 tuple 型別的 cnt 呢?首先 cnt[0] = cnt[0] + 1,雖然有賦值,但是其左邊也是 cnt[0],cnt 是從外邊作用域索引了的。
所以,你看它顯示的結果:此時,cnt 確實也是自由變數的,但是它是不可變物件啊,所以報了 TypeError 錯誤。這下列表為什麼行,你應該知道了。
或者你使用 nonolocal 關鍵字,這個關鍵字的用法與 global 很像,讓你能夠給外部作用域(非全域性作用域)內的變數賦值。它可以使得一個被賦值的區域性變數變為自由變數,並且 nonlocal宣告的變數發生變化時,__closure__中儲存的值也會發生變化:
def counter(FIRST=0): cnt = FIRST # number def add_one(): nonlocal cnt cnt += 1 return cnt return add_one num5 = counter(5) print(num5.__closure__) print(num5.__code__.co_freevars) print(num5())
(<cell at 0x01BFFE30: int object at 0x53E064D0>,) ('cnt',) 6
nonlocal 和 global
def scope_test(): spam = "test spam" def do_nonlocal(): nonlocal spam spam = "nonlocal spam" def do_global(): global spam spam = "global spam" do_nonlocal() print("After nonlocal assignment:", spam) # nonlocal spam do_global() print("After global assignment:", spam) # nonlocal spam scope_test() print("In global scope:", spam) # global spam
After nonlocal assignment: nonlocal spam After global assignment: nonlocal spam In global scope: global spam
lambda 自由引數之坑,特別是和列表解析或for迴圈結合使用時。lambda para_list : expression == > def (para_list): return expression
#---CASE1 fs = [lambda j:i*j for i in range(3)] print([f(2) for f in fs]) #---CASE2 fs = map(lambda i:(lambda j: i*j), range(3)) print([f(2) for f in fs]) #---CASE3 fs = [(lambda i:lambda j:i*j)(i) for i in range(3)] print([f(2) for f in fs])
[4, 4, 4] [0, 2, 4] [0, 2, 4]
首先,CASE1 和 CASE3 顯然都是每回圈一次,就新增一個 lambda 函數到列表中,不同的是,CASE1 新增的 lambda 函數中的 i 每次並沒有接收 for 迴圈中 i 的值,它只是定義的時候指了下 i,所以說,CASE1 中的幾個 lambda 函數的 i,是最後呼叫的時候,也就是 f(2) 時才到外層作用域找它的值的,此時找到的 i 的值就是裡面 for 迴圈結束時的 i 的值。CASE3 則是一開始定義、新增的時候就給 i 賦好了初值。CASE2 則是因為 map 每次迭代的時候都會將一個可迭代物件的元素傳給了 i,所以 CASE2 裡面的每個 lambda 函數的 i 也是各有各的值的。
像這種 lambda 的自由引數的問題的話,如果你不是故意這麼做的話,還是轉為預設引數的好:
fs = [lambda x: x+i for i in range(3)] print([f(2) for f in fs]) fs = [lambda x, i=i: x+i for i in range(3)] print([f(2) for f in fs])
[4, 4, 4] [2, 3, 4]
另外,就是列表解析裡面的作用域是一個全新的作用域,和普通的 for 迴圈則有所不同:
#---CASE4 fs = [lambda j:i*j for i in range(3)] print([f(2) for f in fs]) i = 4 print([f(2) for f in fs]) #---CASE5 fs = [] for i in range(3): fs.append(lambda j:i*j) print([f(2) for f in fs]) i = 4 print([f(2) for f in fs])
[10, 10, 10] [10, 10, 10] [10, 10, 10] [8, 8, 8]
裝飾器
惰性求值,比較常見的是在資料庫存取的時候,可參考 Django 的 queryset 的實現
需要對某個函數的引數提前賦值的情況;當然也可以使用 functools.parial 的偏函數:functools.partial(func, *args, **kw),返回一個 partial 函數物件。
# y = a*x + b, a 和 b 可能只出現一次, x 會出現多次 def line(a, b, x): return a*x + b print(line(3, 4, 5)) print(line(3, 4, 6)) print(line(7, 4, 5)) print(line(7, 4, 6)) # 2.使用閉包 def line(a, b): def value(x): return a*x + b return value # y = 3x + 4 line1 = line(3, 4) print(line1(5)) print(line1(6)) print(line1(7)) # y = 9x + 7 line2 = line(9, 7) print(line2(5)) print(line2(6)) print(line2(7)) # 3.使用 functools.partial 偏函數 from functools import partial line3 = partial(line, 3) print(line3) # functools.partial(<function line at 0x011237C8>, 3) print(line3(4, 5)) line4 = partial(line, 3, 4) print(line4(5)) print(line4(6)) print(line4(7)) line5 = partial(line, 9, 7) print(line5(5)) print(line5(6)) print(line5(7))
簡單總結functools.partial的作用就是:其能把一個函數的某些引數給固定住(也就是設定預設值),並返回一個新的函數,呼叫這個新函數會更簡單。
到此這篇關於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