1. 程式人生 > >python 第3章 之三 閉包,模塊等

python 第3章 之三 閉包,模塊等

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時輸出結果為:
chiyu@chiyu-PC:~$ 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章 之三 閉包,模塊等