九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
關(guān)于Python閉包的一切

?

任何把函數(shù)當(dāng)做一等對象的語言,它的設(shè)計(jì)者都要面對一個問題:作為一等對象的函數(shù)在某個作用域中定義,但是可能會在其他作用域中調(diào)用,如何處理自由變量?
自由變量(free variable),未在局部作用域中綁定的變量。
為了解決這個問題,Python之父Guido Van Rossum設(shè)計(jì)了閉包,有如神來之筆,代碼美學(xué)盡顯。在討論閉包之前,有必要先了解Python中的變量作用域。

變量作用域

先看一個全局變量和自由變量的示例:
>>> b = 6
>>> def f1(a):
... print(a)
... print(b)
...
>>> f1(3)
3
6

函數(shù)體外的b為全局變量,函數(shù)體內(nèi)的b為自由變量。因?yàn)樽杂勺兞縝綁定到了全局變量,所以在函數(shù)f1()中能正確print。
如果稍微改一下,那么函數(shù)體內(nèi)的b就會從自由變量變成局部變量
>>> b = 6
def f1(a):
... print(a)
... print(b)
... b = 9
...
>>> f1(3)
3
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 3, in f1
UnboundLocalError: local variable 'b' referenced before assignment

在函數(shù)f1()后面加上b = 9報(bào)錯:局部變量b在賦值前進(jìn)行了引用。
這不是缺陷,而是Python設(shè)計(jì):Python不要求聲明變量,而是假定在函數(shù)定義體中賦值的變量是局部變量
如果想讓解釋器把b當(dāng)做全局變量,那么需要使用global聲明:
>>> b = 6
>>> def f1(a):
... global b
... print(a)
... print(b)
... b = 9
...
>>> f1(3)
3
6

閉包

回到文章開頭的自由變量問題,假如有個叫做avg的函數(shù),它的作用是計(jì)算系列值的均值,用類實(shí)現(xiàn):
class Averager():

def __init__(self):
self.series = []

def __call__(self, new_value):
self.series.append(new_value)
total = sum(self.series)
return totle / len(self.series)

avg = Averager()
avg(10) # 10.0
avg(11) # 10.5
avg(12) # 11.0
類實(shí)現(xiàn)不存在自由變量問題,因?yàn)閟elf.series是類屬性。但是函數(shù)實(shí)現(xiàn),進(jìn)行函數(shù)嵌套時,問題就出現(xiàn)了:
def make_averager():
series = []

def averager(new_value):
# series是自由變量
series.append(new_value)
total = sum(series)
return totle / len(series)

return averager

avg = make_averager()
avg(10) # 10.0
avg(11) # 10.5
avg(12) # 11.0
函數(shù)make_averager()在局部作用域中定義了series變量,它的內(nèi)部函數(shù)averager()的自由變量series綁定了這個值。但是在調(diào)用avg(10)時,make_averager()函數(shù)已經(jīng)return返回了,它的局部作用域也消失了。沒有閉包的話,自由變量series一定會報(bào)錯找不到定義。
那么閉包是怎么做的呢?閉包是一種函數(shù),它會保留定義時存在的自由變量的綁定,這樣調(diào)用函數(shù)時,雖然定義作用域不可用了,但是仍然能使用那些綁定。
如下圖所示:
閉包會保留自由變量series的綁定,在調(diào)用avg(10)時繼續(xù)使用這個綁定,即使make_averager()函數(shù)的局部作用域已經(jīng)消失。

nonlocal

把上面示例的需求稍微優(yōu)化下,只存儲目前的總值和元素個數(shù):
def make_averager():
count = 0
total = 0

def averager(new_value):
count += 1
total += new_value
return total / count

return averager
運(yùn)行后會報(bào)錯:局部變量count在賦值前進(jìn)行了引用。因?yàn)閏ount +=1等同于count = count + 1,存在賦值,count就變成局部變量了。total也是如此。
這里如果把count和total通過global關(guān)鍵字聲明為全局變量,顯然是不合適的,它們作用域最多只擴(kuò)展到make_averager()函數(shù)內(nèi)。為了解決這個問題,Python3引入了nonlocal關(guān)鍵字聲明:
def make_averager():
count = 0
total = 0

def averager(new_value):
nonlocal count, total
count += 1
total += new_value
return total / count

return averager
nonlocal的作用是把變量標(biāo)記為自由變量,即使在函數(shù)中為變量賦值了,也仍然是自由變量。
注意,對于列表、字典等可變類型來說,添加元素不是賦值,不會隱式創(chuàng)建局部變量。對于數(shù)字、字符串、元組等不可變類型以及None來說,賦值會隱式創(chuàng)建局部變量。示例:
def make_averager():
# 可變類型
count = {}

def averager(new_value):
print(count) # 成功
count[new_value] = new_value
return count

return averager
可變對象添加元素不是賦值,不會隱式創(chuàng)建局部變量。
def make_averager():
# 不可變類型
count = 1

def averager(new_value):
print(count) # 報(bào)錯
count = new_value
return count

return averager
count是不可變類型,賦值會隱式創(chuàng)建局部變量,報(bào)錯:局部變量count在賦值前進(jìn)行了引用。
def make_averager():
# None
count = None

def averager(new_value):
print(count) # 報(bào)錯
count = new_value
return count

return averager
count是None,賦值會隱式創(chuàng)建局部變量,報(bào)錯:局部變量count在賦值前進(jìn)行了引用。

小結(jié)

本文先介紹了全局變量、自由變量、局部變量的概念,這是理解閉包的前提。閉包就是用來解決函數(shù)嵌套時,自由變量如何處理的問題,它會保留自由變量的綁定,即使局部作用域已經(jīng)消失。對于不可變類型和None來說,賦值會隱式創(chuàng)建局部變量,把自由變量轉(zhuǎn)換為局部變量,這可能會導(dǎo)致程序報(bào)錯:局部變量在賦值前進(jìn)行了引用。除了使用global聲明為全局變量外,還可以使用nonlocal聲明把局部變量強(qiáng)制變?yōu)樽杂勺兞浚瑢?shí)現(xiàn)閉包。
參考資料:
《流暢的Python》
你永遠(yuǎn)不知道下個一鍵三連是什么味道~
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Python 中的函數(shù)裝飾器和閉包
5分鐘掌握Python閉包
全菊變量和菊部變量
python global、locals()、nonlocal
這一篇把Python 作用域的疑惑終于解決了
python函數(shù)基礎(chǔ)與python函數(shù)的參數(shù)細(xì)節(jié)!
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服