這里根據(jù)我們平常見到的一些Python常見面試題來跟大家說一下關于Python閉包的相關問題! 題目: 1.什么是閉包?閉包的定義? 2.手寫一個簡單的閉包 3.變量作用域規(guī)則與 nonlocal 關鍵字? 4.閉包的應用 答案要點: 1.首先,我們要了解變量作用域 # 示例一 學過其他語言,比如 Java ,對示例三的結果會比較驚訝,在 Java 中類似的情況,不會報錯,會引用外部的全局變量,而如果在內部重新賦值后,再次使用則會用局部變量的值。而在 Python 中情況則不一樣,它在編譯函數(shù)時,發(fā)現(xiàn)對 b 有賦值的操作,它判定 b 是一個局部變量,所以在打印 b 時,它會去查詢局部變量b,發(fā)現(xiàn)并沒有賦值,所以會拋出異常。 引用《流暢的Python》中對此的解釋:
上段話第一次看可能會有點不明白,其實簡單來說,Python 就是這樣設計的,它認為在函數(shù)體中,如果對變量有賦值操作,則證明這個變量是一個局部變量,并且它只會從局部變量中去讀取數(shù)據(jù)。這樣設計可以避免我們在不知道的情況下,獲取到全局變量的值,從而導致一些錯誤數(shù)據(jù)的出現(xiàn)。 至于解決方法,就是使用 global 關鍵字,來說明我們使用的是 全局變量 。示例如下: b = 6 2.閉包的定義:簡單來說,閉包的概念就是當我們在函數(shù)內定義一個函數(shù)時,這個內部函數(shù)使用了外部函數(shù)的臨時變量,且外部函數(shù)的返回值是內部函數(shù)的引用時,我們稱之為閉包。有點繞 代碼如下: # 一個簡單的實現(xiàn)計算平均值的代碼 3.nonlocal 關鍵字。上面的代碼,有一個小缺陷,有很多重復的計算,當我們傳入一個新的值想要得到新的平均值時,其他前一次的總和是可以通過外部臨時變量存儲的。于是我們很自然的想到下面的代碼: # 一個簡單的實現(xiàn)計算平均值的代碼改進版一 這里報錯的原因,請看第 1 點:變量的作用規(guī)則。因為 scores += val ,其實就是 scores = scores + val,有了賦值操作,則認為 scores 是局部變量了。而我們也沒辦法使用 global 關鍵字,因為此時 scores 和 count 是定義在 get_ave 函數(shù)內的,它們倆也是一個局部變量。而為什么我們使用 list 時,沒有出現(xiàn)這個問題呢?也是很好理解的,因為我們使用的是 list.append() 方法,它沒有賦值操作。你可以簡單認為,可變對象(即我們可以通過調用自身一些方法去做增刪改操作且變量地址值不變)不存在此問題,而不可變對象則會有。 在 Python 3 中引入了一個關鍵詞 nonlocal 解決了這一個問題: # 一個簡單的實現(xiàn)計算平均值的代碼改進版二 你也許會說,那在 Python 2 的環(huán)境下應該怎么解決呢?emm~其實呢 也是有辦法的,思路就是將不可變對象變?yōu)榭勺儗ο蠹纯伞>唧w代碼如下: # -*- coding:utf-8 -*- 4.閉包的應用:首先是裝飾器,裝飾器就是通過修改被裝飾函數(shù),來達到增加新功能的作用。當我們在內部函數(shù)去修改被裝飾函數(shù)時,大部分情況都會使用到閉包。簡單示例: def decorator(func): # 外部函數(shù)的局部變量 func 另外一個應用由之前求平均值的示例也可以看出來,可以在重復計算時提高效率。其次還有一個比較重要的應用場景,就是利用“惰性求值”這一特性,這一點在 Django 的 QuerySet 里有體現(xiàn)。當我們利用 ORM 去做 SQL 查詢時,很多時候會根據(jù)不同的判斷條件,去加 filter,加 filter 的時候并沒有真正做查詢,在最終獲取結果的時候才真正執(zhí)行了查詢。 這里也是根據(jù)我們平常見到的一些Python常見面試題來跟大家講的Python閉包,有補充的伙伴非常歡迎留言補充哈!也希望大家能夠互相學習,取長補短,進步呀! |
|
來自: 千鋒Python學堂 > 《Python基礎教程分享》