什麼是閉包呢?這裡借用《Fluent Python》中對閉包的定義 "a closure is function with an extended scope that encompasses non-global variables referenced in the body of the function but not defined there"。簡單來說:閉包=函數+自由變量的引用。
那麼什麼是自由變量(free variables)?在一個函數中,如果某個變量既不是在函數內部創建的也不屬於函數的形參,並且它也不是全局變量(global variables),那麼這個變量對該函數來說就是自由變量。說的太多不如給一個例子:
def outer():
var = 3
def inner():
print(var)
return inner
func = outer()
func() # print 3
var = 5
func() # print 3
這段代碼中,函數inner和var就構成了閉包,var對於inner來說就是自由變量。可以看到var不是在inner中創建的,而是在outer中創建。那麼函數inner如何存儲變量var呢?答案就是inner.__code__.co_freevars和inner.__closure__,前者記錄了自由變量的名字,後者記錄了自由變量的值。
print func.__code__.co_freevars # ('var',)
print func.__closure__[0].cell_contents # 3
閉包單元指的是函數所需的值,但取自周圍的范圍。
當 Python 編譯嵌套函數時,它會記下它引用但僅在嵌套函數和父范圍的代碼對象中的父函數(不是全局變量)中定義的任何變量。這些分別是這些函數的 __code__ 對象上的 co_freevars 和 co_cellvars 屬性。
然後,當您實際創建嵌套函數(執行父函數時發生)時,這些引用將用於將閉包附加到嵌套函數。
函數閉包包含一個單元元組,每個單元對應一個自由變量(以 co_freevars 命名);單元格是對父作用域的局部變量的特殊引用,它遵循那些局部變量指向的值。最好用一個例子來說明這一點:
def foo():
def bar():
print(spam)
spam = 'ham'
bar()
spam = 'eggs'
bar()
return bar
b = foo()
b()
在上面的例子中,函數 bar 有一個閉包單元,它指向函數 foo 中的垃圾郵件。單元格遵循垃圾郵件的值。更重要的是,一旦 foo() 完成並返回 bar,即使 foo 中的變量 spam 不再存在,單元格仍會繼續引用該值(字符串雞蛋)。
因此,上面的代碼輸出:
>>> b=foo()
ham
eggs
>>> b()
eggs
此時, b.__closure__[0].cell_contents
是 'eggs'
.
請注意,調用 bar() 時會取消引用閉包;閉包並沒有捕捉到這裡的價值。當您生成引用循環變量的嵌套函數(使用 lambda 表達式或 def 語句)時,這會有所不同:
def foo():
bar = []
for spam in ('ham', 'eggs', 'salad'):
bar.append(lambda: spam)
return bar
for bar in foo():
print bar()
上面的他將連續打印 3 次沙拉,因為所有三個 lambda 函數都引用 spam 變量,而不是創建函數對象時綁定的值。當 for 循環結束時,垃圾郵件被綁定到“沙拉”,因此所有三個閉包都將解析為該值。
>>>python.__closure__
(<cell at 0x67f50: str object at 0x69230>,)
>>>python.__closure__[0].cell_contents