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.閉包函式自帶函式作用域
正常意義上的函式,在函式執行過程中查詢變數的順序是一層一層向外找,符合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函式閉包Python函式
- Python 閉包函式說明Python函式
- 函式閉包函式
- 閉包函式函式
- 『無為則無心』Python函式 — 35、Python中的閉包Python函式
- 閉包函式(匿名函式)的理解函式
- Python基礎之閉包函式Python函式
- js中的函式巢狀和閉包JS函式巢狀
- go 閉包函式Go函式
- js函式閉包JS函式
- python 關於 函式物件與閉包Python函式物件
- 回撥函式 與 函式閉包函式
- JS閉包函式概念JS函式
- JavaScript 匿名函式 閉包JavaScript函式
- js函式 函式自呼叫 返回函式的函式 (閉包)JS函式
- 閉包詳解二:JavaScript中的高階函式JavaScript函式
- JS函式表示式——函式遞迴、閉包JS函式遞迴
- JS閉包函式和回撥函式JS函式
- PHP 中的匿名函式和閉包基礎學習PHP函式
- js中,函式的閉包、作用域跟[[Scopes]]的關係JS函式
- 聊聊 Python 中的閉包Python
- JavaScript進階系列01,函式的宣告,函式引數,函式閉包JavaScript函式
- python基礎知識之函式初階——閉包Python函式
- 美麗的閉包,在js中實現函式過載JS函式
- JavaScript4:函式和閉包JavaScript函式
- 3. 匿名函式與閉包函式
- 淺談匿名函式和閉包函式
- 立即執行函式(IIFE)&&閉包函式
- PHP閉包函式使用詳解PHP函式
- 一個閉包函式的簡單例子函式單例
- 說說Python中的閉包Python
- Python 中的閉包總結Python
- Python中的閉包總結Python
- 一個常見的閉包函式的分析函式
- PHP新特性之閉包、匿名函式PHP函式
- 1.13 JavaScript4:函式和閉包JavaScript函式
- PHP 回撥、匿名函式和閉包PHP函式
- 閉包函式,裝飾器詳解函式