Python的閉包:是一種定義在某個作用域D
中的函數F
,這種函數F
引用了作用域D
裏面的變量。
案例1:將values
列表中的數字進行排序,同時位於group
中的數字放在不屬於group
中元素之前。
def sort_priority(values,group):
def helper(x):
if x in group: #引用了上層作用域中的變量
return (0,x)
return (1,x)
values.sort(key=helper)
- Python的函數屬於一級對象,所以可以直接引用函數、把函數賦給變量、把函數當作參數傳給其他函數,在上述例子中,將
helper
閉包函數傳給了sort
方法的key
參數。 - Python在對兩個元組進行排序時,首先比較位置0處的元素大小,如果相等,再比較位置1處的元素大小,如果還是相等,則比較位置2處的元素,依次類推。
案例2:在案例1的基礎上,返回values
中是否出現了在group
中的元素標誌位found
。
def sort_priority(values,group):
found = False
def helper(x):
if x in group:
found = True
return (0,x)
return (1,x)
values.sort(key=helper)
return found
if __name__ == "__main__":
values = [1,2,4,9,92,100]
group = [1,2,100]
found = sort_priority(values,group)
print(values)
print(found)
輸出結果:
[1, 2, 100, 4, 9, 92]
False
可以看出,排序的結果是對的,found
的返回值應該是True
,但卻是False
。
這就需要考慮Python在引用變量和賦值時的規則。
1.引用變量時,Python解釋器遍歷作用域的順序:
- 當前函數的作用域
- 任何外圍作用域(例如,包含當前函數的其他函數)
- 包含當前代碼的模塊的作用域(也叫全局作用域,
global scope
) - 內置作用域(也就是包含
len
和str
等函數的作用域)
2.給變量賦值時,規則則有所不同。
如果當前作用域已經定義了這個變量,那麼該變量就會具備新值。若是當前作用於內沒有這個變量,Python則會把這次賦值視爲對該變量的定義,使其稱爲局部變量。
可以看出,案例2中的helper
函數中的found = True
,被視爲創建了helper
函數作用域內的局部變量,而並非外部變量(包含helper
函數的sort_priotity
)found
。
爲了避免這種情況的存在,可以採用nonlocal
關鍵字,在helper
函數中指定found
變量,這樣Python會在上層作用域中查找該變量。
def sort_priority(values,group):
found = False
def helper(x):
if x in group:
nonlocal found ###
found = True
return (0,x)
return (1,x)
values.sort(key=helper)
return found
if __name__ == "__main__":
values = [1,2,4,9,92,100]
group = [1,2,100]
found = sort_priority(values,group)
print(values)
print(found)
nonlocal
語句清楚的表明:如果在閉包內給該變量賦值,那麼修改的是閉包外的那個作用域中的變量。這與global
語句互爲補充,global
用來表示修改模塊作用域中的變量。
主要區別:
global
修改全局變量(模塊作用域中的變量)nonlocal
修改閉包作用域外的變量
注意點:除了簡單的函數外,儘量不要用nonlocal
語句,否則遭到濫用,很難追蹤代碼異常。
如果使用nonlocal
的代碼,已經寫的越來越複雜了, 可以考慮將相關的函數封裝成輔助類。
class Sorter(object):
def __init__(self,group):
super().__init__()
self.group = group
self.found = False
def __call__(self,x):
if x in self.group:
self.found = True
return (0,x)
return (1,x)
if __name__ == "__main__":
values = [1,2,4,9,92,100]
group = [1,2,100]
sorter = Sorter(group)
values.sort(key=sorter)
print(sorter.found)