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