摘要:項(xiàng)目地址閉包在計(jì)算機(jī)科學(xué)中,閉包英語,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。
項(xiàng)目地址:https://git.io/pytips
閉包(Closure)在計(jì)算機(jī)科學(xué)中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數(shù)閉包(function closures),是引用了自由變量的函數(shù)。這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使已經(jīng)離開了創(chuàng)造它的環(huán)境也不例外。
[維基百科::閉包(計(jì)算機(jī)科學(xué))]
0x02 Python 中的函數(shù)式編程 本來也應(yīng)該包括閉包的概念,但是我覺得閉包更重要的是對(duì)作用域(Scope)的理解,因此把它多帶帶列出來,同時(shí)可以理順一下 Python 的作用域規(guī)則。
閉包的概念最早出現(xiàn)在函數(shù)式編程語言中,后來被一些命令式編程語言所借鑒。尤其是在一些函數(shù)作為一等公民的語言中,例如JavaScript就經(jīng)常用到(在JavaScript中函數(shù)幾乎可以當(dāng)做“特等公民”看待),我之前也寫過一篇關(guān)于JavaScript閉包的文章(圖解Javascript上下文與作用域),實(shí)際上閉包并不是太復(fù)雜的概念,但是可以借助閉包更好地理解不同語言的作用域規(guī)則。
命名空間與作用域0x00 The Zen of Python的最后一句重點(diǎn)強(qiáng)調(diào)命名空間的概念,我們可以把命名空間看做一個(gè)大型的字典類型(Dict),里面包含了所有變量的名字和值的映射關(guān)系。在 Python 中,作用域?qū)嶋H上可以看做是“在當(dāng)前上下文的位置,獲取命名空間變量的規(guī)則”。在 Python 代碼執(zhí)行的任意位置,都至少存在三層嵌套的作用域:
最內(nèi)層作用域,最早搜索,包含所有局部變量(Python 默認(rèn)所有變量聲明均為局部變量)
所有包含當(dāng)前上下文的外層函數(shù)的作用域,由內(nèi)而外依次搜索,這里包含的是非局部也非全局的變量
一直向上搜索,直到當(dāng)前模塊的全局變量
最外層,最后搜索的,內(nèi)置(built-in)變量
在任意執(zhí)行位置,可以將作用域看成是對(duì)下面這樣一個(gè)命名空間的搜索:
scopes = { "local": {"locals": None, "non-local": {"locals": None, "global": {"locals": None, "built-in": ["built-ins"]}}}, }
除了默認(rèn)的局部變量聲明方式,Python 還有global和nonlocal兩種類型的聲明(nonlocal是Python 3.x之后才有,2.7沒有),其中 global 指定的變量直接指向(3)當(dāng)前模塊的全局變量,而nonlocal則指向(2)最內(nèi)層之外,global以內(nèi)的變量。這里需要強(qiáng)調(diào)指向(references and assignments)的原因是,普通的局部變量對(duì)最內(nèi)層局部作用域之外只有只讀(read-only)的訪問權(quán)限,比如下面的例子:
x = 100 def main(): x += 1 print(x) main()
--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last)in () 3 x += 1 4 print(x) ----> 5 main() in main() 1 x = 100 2 def main(): ----> 3 x += 1 4 print(x) 5 main()
UnboundLocalError: local variable "x" referenced before assignment
這里拋出UnboundLocalError,是因?yàn)?b>main()函數(shù)內(nèi)部的作用域?qū)τ谌肿兞?b>x僅有只讀權(quán)限,想要在main()中對(duì)x進(jìn)行改變,不會(huì)影響全局變量,而是會(huì)創(chuàng)建一個(gè)新的局部變量,顯然無法對(duì)還未創(chuàng)建的局部變量直接使用x += 1。如果想要獲得全局變量的完全引用,則需要global聲明:
x = 100 def main(): global x x += 1 print(x) main() print(x) # 全局變量已被改變
101 101Python 閉包
到這里基本上已經(jīng)了解了 Python 作用域的規(guī)則,那么我們來仿照 JavaScript 寫一個(gè)計(jì)數(shù)器的閉包:
""" /* JavaScript Closure example */ var inc = function(){ var x = 0; return function(){ console.log(x++); }; }; var inc1 = inc() var inc2 = inc() """ # Python 3.5 def inc(): x = 0 def inner(): nonlocal x x += 1 print(x) return inner inc1 = inc() inc2 = inc() inc1() inc1() inc1() inc2()
1 2 3 1
對(duì)于還沒有nonlocal關(guān)鍵字的 Python 2.7,可以通過一點(diǎn)小技巧來規(guī)避局部作用域只讀的限制:
# Python 2.7 def inc(): x = [0] def inner(): x[0] += 1 print(x[0]) return inner inc1 = inc() inc2 = inc() inc1() inc1() inc1() inc2()
1 2 3 1
上面的例子中,inc1()是在全局環(huán)境下執(zhí)行的,雖然全局環(huán)境是不能向下獲取到inc()中的局部變量x的,但是我們返回了一個(gè)inc()內(nèi)部的函數(shù)inner(),而inner()對(duì)inc()中的局部變量是有訪問權(quán)限的。也就是說inner()將inc()內(nèi)的局部作用域打包送給了inc1和inc2,從而使它們各自獨(dú)立擁有了一塊封閉起來的作用域,不受全局變量或者任何其它運(yùn)行環(huán)境的影響,因此稱為閉包。
閉包函數(shù)都有一個(gè)__closure__屬性,其中包含了它所引用的上層作用域中的變量:
print(inc1.__closure__[0].cell_contents) print(inc2.__closure__[0].cell_contents)
[3] [1]參考
9.2. Python Scopes and Namespaces
Visualize Python Execution
Wikipedia::Closure
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/37791.html
摘要:不可變對(duì)象包括,,,,等,可變對(duì)象包括,,等。在中,賦值的過程僅僅是創(chuàng)建一個(gè)某個(gè)值的對(duì)象將變量名指向引用這個(gè)對(duì)象。這就像語言中指針的概念,只不過更靈活地是中的變量隨時(shí)可以指向其它對(duì)象不分類型,其它變量也可以指向這一對(duì)象。 項(xiàng)目地址:https://git.io/pytips Python 中的對(duì)象分為兩種:可變對(duì)象(mutable)和不可變對(duì)象(immutable)。不可變對(duì)象包括in...
摘要:項(xiàng)目地址引入了語句與上下文管理器類型,其主要作用包括保存重置各種全局狀態(tài),鎖住或解鎖資源,關(guān)閉打開的文件等。了解了語句的執(zhí)行過程,我們可以編寫自己的上下文管理器。生成器的寫法更簡(jiǎn)潔,適合快速生成一個(gè)簡(jiǎn)單的上下文管理器。 項(xiàng)目地址:https://git.io/pytips Python 2.5 引入了 with 語句(PEP 343)與上下文管理器類型(Context Manager ...
摘要:項(xiàng)目地址時(shí)間和日期可能涉及到不同的時(shí)區(qū)格式,同時(shí)又經(jīng)常需要作為時(shí)間戳保存,有時(shí)候還需要進(jìn)行一些加減操作,因此處理起來通常會(huì)因?yàn)榉椒ㄌ喽鵁o從下手。中與時(shí)間和日期相關(guān)的標(biāo)準(zhǔn)庫有個(gè)和。 項(xiàng)目地址:https://git.io/pytips 時(shí)間和日期可能涉及到不同的時(shí)區(qū)、格式,同時(shí)又經(jīng)常需要作為時(shí)間戳保存,有時(shí)候還需要進(jìn)行一些加減操作,因此處理起來通常會(huì)因?yàn)榉椒ㄌ喽鵁o從下手。Python...
摘要:項(xiàng)目地址所有用過的人應(yīng)該都看過下面兩行錯(cuò)誤信息這就是界的錕斤拷今天和接下來幾期的內(nèi)容將主要關(guān)注中的字符串字節(jié)及兩者之間的相互轉(zhuǎn)換。 項(xiàng)目地址:https://git.io/pytips 所有用過 Python (2&3)的人應(yīng)該都看過下面兩行錯(cuò)誤信息: UnicodeEncodeError: ascii codec cant encode characters in position...
摘要:這里的關(guān)鍵詞函數(shù)必須明確指明,不能通過位置推斷則代表任意數(shù)量的關(guān)鍵詞參數(shù)添加的新特性,使得可以在函數(shù)參數(shù)之外使用這里的逗號(hào)不能漏掉所謂的解包實(shí)際上可以看做是去掉的元組或者是去掉的字典。 項(xiàng)目地址:https://git.io/pytips 函數(shù)調(diào)用的參數(shù)規(guī)則與解包 Python 的函數(shù)在聲明參數(shù)時(shí)大概有下面 4 種形式: 不帶默認(rèn)值的:def func(a): pass 帶有默認(rèn)值的...
閱讀 2448·2021-11-15 11:38
閱讀 2831·2021-11-02 14:44
閱讀 3812·2021-09-26 10:13
閱讀 3055·2021-08-13 15:02
閱讀 776·2019-08-30 15:56
閱讀 1427·2019-08-30 15:53
閱讀 2357·2019-08-30 13:01
閱讀 3184·2019-08-29 12:57