第15條 閉包內使用外圍作用域的變量

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
  • 內置作用域(也就是包含lenstr等函數的作用域)

2.給變量賦值時,規則則有所不同。

如果當前作用域已經定義了這個變量,那麼該變量就會具備新值。若是當前作用於內沒有這個變量,Python則會把這次賦值視爲對該變量的定義,使其稱爲局部變量。

可以看出,案例2中的helper函數中的found = True,被視爲創建了helper函數作用域內的局部變量,而並非外部變量(包含helper函數的sort_priotityfound

爲了避免這種情況的存在,可以採用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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章