標籤:python
閉包&LEGB法則
所謂閉包,就是將組成函數的語句和這些語句的執行環境打包在一起時,得到的對象
聽上去的確有些複雜,還是用一個栗子來協助理解一下。假設我們在foo.py模組中做了如下定義:
#foo.py
filename = "foo.py"
def call_func(f):
return f() #如前面介紹的,f引用一個函數對象,然後調用它
1
2
3
4
5
在另一個func.py模組中,寫下了這樣的代碼:
#func.py
import foo #匯入foo.py
filename = "func.py"
def show_filename():
return "filename: %s" % filename
if name == "main":
print foo.call_func(show_filename) #注意:實際發生調用的位置,是在foo.call_func函數中
1
2
3
4
5
6
7
8
9
當我們用python func.py命令執行func.py時輸出結果為:
[email protected]:~$ python func.py
filename:func.py
1
2
很顯然show_filename()函數使用的filename變數的值,是在與它相同環境(func.py模組)中定義的那個。儘管foo.py模組中也定義了同名的filename變數,而且實際調用show_filename的位置也是在foo.py的call_func內部。
而對於嵌套函數,這一機制則會表現的更加明顯:閉包將會捕捉內層函數執行所需的整個環境:
#enclosed.py
import foo
def wrapper():
filename = "enclosed.py"
def show_filename():
return "filename: %s" % filename
print foo.call_func(show_filename) #輸出:filename: enclosed.py
實際上,每一個函數對象,都有一個指向了該函數定義時所在全域名稱空間的globals屬性:
#show_filename inside wrapper
#show_filename.globals
{
‘builtins‘: <module ‘builtin‘ (built-in)>, #內建範圍環境
‘file‘: ‘enclosed.py‘,
‘wrapper‘: <function wrapper at 0x7f84768b6578>, #直接外圍環境
‘package‘: None,
‘name‘: ‘main‘,
‘foo‘: <module ‘foo‘ from ‘/home/chiyu/foo.pyc‘>, #全域環境
‘doc‘: None
當代碼執行到show_filename中的return “filename: %s” % filename語句時,解析器按照下面的順序尋找filename變數:
1.Local - 本地函數(show_filename)內部,通過任何方式賦值的,而且沒有被global關鍵字聲明為全域變數的filename變數;
2.Enclosing - 直接外圍空間(上層函數wrapper)的本地範圍,尋找filename變數(如果有多層嵌套,則由內而外逐層尋找,直至最外層的函數);
3.Global - 全域空間(模組enclosed.py),在模組頂層賦值的filename變數;
4.Builtin - 內建模組(builtin)中預定義的變數名中尋找filename變數;
在任何一層先找到了符合要求的filename變數,則不再向更外層尋找。如果直到Builtin層仍然沒有找到符合要求的變數,則拋出NameError異常。這就是變數名解析的:LEGB法則。
總結:
1.閉包最重要的使用價值在於:封存函數執行的上下文環境;
2.閉包在其捕捉的執行環境(def語句塊所在上下文)中,也遵循LEGB規則逐層尋找,直至找到符合要求的變數,或者拋出異常。
python 第3章 之三 閉包,模組等