您好,登錄后才能下訂單哦!
Python中什么是閉包函數?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
python中的閉包函數
通常我們定義函數都是這樣定義的:
def foo(): pass
其實在函數式編程中,函數里面還可以嵌套函數,如下面這樣:
def foo(): print("hello world in foo") def bar(): print("hello world in bar")
此時我們調用foo函數,執行結果會是什么樣子的呢??
hello world in foo
結果如上所示,只會執行foo函數的第一層函數,bar函數是不會被執行的。為什么呢?
實際上來說,不管函數寫在哪個部分,那都只是定義了一個函數,只有這個函數被調用,函數內部的語句才會被執行。
在上面的例子中,bar函數雖然在foo函數內部定義了,但是并沒有被執行,所以bar函數是不會被執行的。
這樣說來,定義在一個函數內部的函數就沒什么作用了嗎??其實不是這樣的。
來看下面的例子,把bar函數作為一個值返回給foo函數,來看執行過程:
def foo(): print("hello world in foo") def bar(): print("hello world in bar") return bar f1=foo() print(f1)
此時,由于bar函數作為一個返回值被返回給了foo,所以foo函數執行結果是有返回值的,此時定義一個變量f1來接收foo函數的執行返回結果,然后打印f1。
返回的結果如下:
hello world in foo <function foo.<locals>.bar at 0x0000000002941A60>
可以看到首先打印了foo函數中定義的一個print語句,接著打印的是foo函數中包含的bar函數的內存地址,既然是一個函數的內存地址,當然可以加括號來執行這個函數。
def foo(): print("hello world in foo") def bar(): print("hello world in bar") return bar f1=foo() f1()
此時,這段代碼的執行結果為:
hello world in foo hello world in bar
兩個print語句都被打印出來了。
在上面的例子里,首先定義了一個函數foo,接著在foo函數內部又嵌套定義了一個函數bar,然后返回函數bar的函數名,這就是閉包函數的定義方式。其實,閉包的定義就是一個函數內部又嵌套了一個函數。
來看下面的這段代碼:
def foo(): print("hello world in foo") name="python" def bar(): print(name) print("hello world in bar") return bar f1=foo() f1()
在上面的例子里,在外層函數中定義了一個變量name,然后在內層函數中打印這個變量name。
此時執行上面的代碼,在打印name這個變量的時候,會先在bar函數內部查找name這個變量,但是bar函數里面是沒有name這個變量的,此時根據python查找變量的LEGB法則,會到bar函數的外面一層去繼續查找name這個變量,此時可以找到name這個變量,所以這里打印的foo函數中定義的name的值。
執行上面的代碼,打印結果如下:
hello world in foo python hello world in bar
這里要記住很重要的一點就是:內層函數引用了外層函數的局部變量。
來分析下上面的例子中程序的執行過程:
首先運行foo函數,foo函數的執行結果是返回bar的函數名,此時又把foo函數的執行結果定義給了變量f1,
所以此時f1就等于bar這個函數的內存地址,然后f1加括號運行就表示運行了bar函數。
在執行bar函數的過程中,bar函數訪問到了外層foo函數中定義的變量,這就是一個典型的閉包函數。
那使用閉包函數有什么好處呢??在上面的例子里,f1的值是bar函數的內存地址,f1加括號運行就是在運行bar函數。
又由于f1是一個全局變量,這意味著可以在整個程序的任意位置都可以運行f1函數,此時再定義一個函數,在這個函數內部調用f1函數,
def foo(): print("hello world in foo") name = "python" def bar(): print(name) print("hello world in bar") return bar f1 = foo() def func(): name = "aaaaa" f1() func()
來分析一下程序的執行過程:
1.運行func函數,程序會先在內存中申請一塊空間以保存name變量的值,然后運行f1函數,f1是在全局中定義的變量,所以一定可以找到f1函數的內存地址。
2.f1加括號運行,就是在執行一個閉包函數,這個閉包函數內部引用了name這個變量。
3.name這個變量在bar函數的外部已經定義了,所以在func函數內部調用f1函數,也就是bar函數時,其引用的變量依然是foo函數內部定義的name變量,而不是func函數內部定義的name變量。
4.因為f1函數的內部已經包含了name這個函數的值,所以就算在func函數內部也定義了name這個變量,程序執行的結果打印的依然是foo函數內部定義的name的值。
程序執行結果:
hello world in foo python hello world in bar
怎樣驗證一個函數是閉包函數?首先,閉包函數都有一個特有的屬性:closure。
在上面的例子里,打印f1的__closure__屬性。
def foo(): name = "python" def bar(): print(name) print("hello world in bar") return bar f1 = foo() print(f1.__closure__)
打印結果如下:
(<cell at 0x0000000001DF5708: str object at 0x0000000001E79688>,)
可以看到__closure__屬性的打印結果是一個元組形式的,其值就是f1函數的外層函數作用域,此時可以調用__closure__返回的元組的元素的cell_contents方法打印出name變量的值。
def foo(): name = "python" def bar(): print(name) print("hello world in bar") return bar f1 = foo() print(f1.__closure__[0].cell_contents)
打印結果如下:
python
可以看到程序已經打印出name變量的值了,即然__closure__的返回結果是一個元組,那么這個元組中一定是可以包含多個值的,看下面的例子。
在foo函數內部定義多個變量,然后在bar函數內部打印幾個變量的值,然后運行這個閉包函數,打印閉包函數的__closure__方法。
def foo(): print("hello world in foo") name1 = "python1" name2 = "python2" name3 = "python3" name4 = "python4" def bar(): print(name1) print(name2) print(name3) print(name4) print("hello world in bar") return bar f1 = foo() print(f1.__closure__)
程序執行結果:
(<cell at 0x0000000002145708: str object at 0x00000000021C9260>, <cell at 0x0000000002145A08: str object at 0x00000000021C93B0>, <cell at 0x0000000002145768: str object at 0x000000000295BE30>, <cell at 0x0000000002145C18: str object at 0x0000000002963880>)
由于在foo函數內部定義了4個變量,而且在bar函數內部引用了這4個變量,所以打印這個閉包函數的__closure__方法,返回的元組中就有4個元素。
現在可以分別打印返回的元組中的這4個字符串對象的值了。
def foo(): name1 = "python1" name2 = "python2" name3 = "python3" name4 = "python4" def bar(): print(name1) print(name2) print(name3) print(name4) print("hello world in bar") return bar f1 = foo() print(f1.__closure__[0].cell_contents) print(f1.__closure__[1].cell_contents) print(f1.__closure__[2].cell_contents) print(f1.__closure__[3].cell_contents)
程序執行結果:
python1 python2 python3 python4
那么現在還剩下最后一個問題了,那就是閉包函數的內層函數一定要返回嗎??
來看下面一個例子:
def foo(): name = "python1" def bar(): print(name) print(bar.__closure__) foo()
定義了一個嵌套函數,然后這個嵌套函數的內層函數沒有被返回,而是直接打印內層函數的__closure__方法,然后直接調用外層函數。
程序執行結果:
(<cell at 0x0000000002155708: str object at 0x00000000021D9688>,)
依然打印出了內層函數的引用的變量對象,這說明閉包函數的內層函數還一定要返回。
閉包函數的內層函數可以調用全局變量嗎??
把外層函數內部定義的變量改為全局變量,然后在內層函數中引用這個變量。
name = "python1" def foo(): def bar(): print(name) print(bar.__closure__) f=foo() print(f)
程序執行結果:
None None
可以看到,程序的執行結果是兩個None,嵌套函數的內層函數的__closure__函數的值為None,這說明foo函數的內層嵌套函數bar調用的全局變量沒有成功,所以上面的例子不是一個閉包函數。
關于閉包函數的一些總結:
閉包的定義為:
(1)在函數內部定義的函數,稱為內部函數。
(2)內部函數調用了外部函數的局部變量。
(3)即使內部函數返回了,還是可以使用局部變量。
(4)通常閉包函數的內層函數都要被返回給外部函數。
(5)閉包函數的外部函數可以在任何地方被調用,而不再受函數定義時層級的限制。
閉包函數的作用
1.閉包函數自帶函數作用域
正常意義上的函數,在函數執行過程中查找變量的順序是一層一層向外找,符合LEGB(Local->Enclose->Global->Built in)法則的,
但是對閉包函數來說,查找變量只會找內部函數外面的那一層,因為閉包函數本身就自帶一層作用域,這樣才符合"閉包"兩個字的意思。
2.延遲計算(也叫惰性計算)
看下面的例子
def func(): name="python" def bar(): print(name) return bar f=func() print(f.__closure)
在上面的例子里,執行foo()函數的返回結果是一個包含自帶的某種狀態的函數,實際上這個函數并沒有執行,
以后想執行這個自帶狀態的函數時,把func()返回結果所賦值的那個變量加括號就可以執行了。
3.要想讓一個函數始終保持一種狀態,就可以使用閉包。
例子:
name="python" def func(): print("I like %s" % name) func()
上面的代碼執行結果會打印一行:"I like python"
但是我們知道,在不同的地方調用func函數,打印的結果很大可能是不一樣的,那么如果我想不管在什么地方調用func函數,打印的結果都是"I like python"時,就可以使用閉包了。
def func1(): name="python" def func(): print("I like %s" % name) return func func=func1() func()
如上圖所示,在func函數外面再包含一層函數func1,執行func1函數,再把func1函數的返回結果賦值給func這個變量。
此時func就是一個閉包函數了,把func函數加括號就可以執行了。
而且我們一定知道,此時func函數的執行結果一定會打印"I like python"這句話,而且不管func函數在程序的哪個位置被調用,執行結果都是一樣的。
看完上述內容,你們掌握Python中什么是閉包函數的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。