小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

根據(jù)Python常見面試題來(lái)談?wù)?Python 閉包

 千鋒Python學(xué)堂 2019-07-17

這里根據(jù)我們平常見到的一些Python常見面試題來(lái)跟大家說(shuō)一下關(guān)于Python閉包的相關(guān)問(wèn)題!

題目:

1.什么是閉包?閉包的定義?

2.手寫一個(gè)簡(jiǎn)單的閉包

3.變量作用域規(guī)則與 nonlocal 關(guān)鍵字?

4.閉包的應(yīng)用

根據(jù)Python常見面試題來(lái)談?wù)?Python 閉包

答案要點(diǎn):

1.首先,我們要了解變量作用域

# 示例一
def test1(a):
print(a)
print(b) # 當(dāng)函數(shù)執(zhí)行到這一步時(shí)會(huì)報(bào)錯(cuò)。
# NameError: global name 'b' is not defined
test1(1)
# 示例二
b = 6
def test2(a):
print(a)
print(b)
test2(1) # 1 6
# 示例三
b = 6
def test3(a):
print(a)
print(b) # 當(dāng)函數(shù)執(zhí)行到這一步仍然會(huì)報(bào)錯(cuò)
# UnboundLocalError: local variable 'b' referenced before assignment
b = 9 # 比示例二多了一行賦值
test3(1)

學(xué)過(guò)其他語(yǔ)言,比如 Java ,對(duì)示例三的結(jié)果會(huì)比較驚訝,在 Java 中類似的情況,不會(huì)報(bào)錯(cuò),會(huì)引用外部的全局變量,而如果在內(nèi)部重新賦值后,再次使用則會(huì)用局部變量的值。而在 Python 中情況則不一樣,它在編譯函數(shù)時(shí),發(fā)現(xiàn)對(duì) b 有賦值的操作,它判定 b 是一個(gè)局部變量,所以在打印 b 時(shí),它會(huì)去查詢局部變量b,發(fā)現(xiàn)并沒有賦值,所以會(huì)拋出異常。

引用《流暢的Python》中對(duì)此的解釋:

這不是缺陷,而是設(shè)計(jì)選擇:Python 不要求聲明變量,但是假定在函數(shù)定義體中賦值的變量是局部變量。這比 JavaScript 的行為要好多了,JavaScript 也不要求聲明變量,但是如果忘記把變量聲明為局部變量(使用var),可能會(huì)在不知情的情況下獲取全局部變量。

上段話第一次看可能會(huì)有點(diǎn)不明白,其實(shí)簡(jiǎn)單來(lái)說(shuō),Python 就是這樣設(shè)計(jì)的,它認(rèn)為在函數(shù)體中,如果對(duì)變量有賦值操作,則證明這個(gè)變量是一個(gè)局部變量,并且它只會(huì)從局部變量中去讀取數(shù)據(jù)。這樣設(shè)計(jì)可以避免我們?cè)诓恢赖那闆r下,獲取到全局變量的值,從而導(dǎo)致一些錯(cuò)誤數(shù)據(jù)的出現(xiàn)。

至于解決方法,就是使用 global 關(guān)鍵字,來(lái)說(shuō)明我們使用的是 全局變量 。示例如下:

b = 6
def test4(a):
print(a)
global b # 1
print(b) # 6
b = 9
print(b) # 9
test4(1)

2.閉包的定義:簡(jiǎn)單來(lái)說(shuō),閉包的概念就是當(dāng)我們?cè)诤瘮?shù)內(nèi)定義一個(gè)函數(shù)時(shí),這個(gè)內(nèi)部函數(shù)使用了外部函數(shù)的臨時(shí)變量,且外部函數(shù)的返回值是內(nèi)部函數(shù)的引用時(shí),我們稱之為閉包。有點(diǎn)繞

代碼如下:

# 一個(gè)簡(jiǎn)單的實(shí)現(xiàn)計(jì)算平均值的代碼

def get_avg():
scores = [] # 外部臨時(shí)變量

def inner_count_avg(val): # 內(nèi)部函數(shù),用于計(jì)算平均值
scores.append(val) # 使用外部函數(shù)的臨時(shí)變量
return sum(scores) / len(scores) # 返回計(jì)算出的平均值

return inner_count_avg # 外部函數(shù)返回內(nèi)部函數(shù)引用

avg = get_avg()
print(avg(10)) # 10
print(avg(11)) # 10.5
...

3.nonlocal 關(guān)鍵字。上面的代碼,有一個(gè)小缺陷,有很多重復(fù)的計(jì)算,當(dāng)我們傳入一個(gè)新的值想要得到新的平均值時(shí),其他前一次的總和是可以通過(guò)外部臨時(shí)變量存儲(chǔ)的。于是我們很自然的想到下面的代碼:

# 一個(gè)簡(jiǎn)單的實(shí)現(xiàn)計(jì)算平均值的代碼改進(jìn)版一
def get_avg():
scores = 0 # 將外部臨時(shí)變量由 list 改為一個(gè) 整型數(shù)值
count = 0 # 同時(shí)新增一個(gè)變量,記錄個(gè)數(shù)
def inner_count_avg(val): # 內(nèi)部函數(shù),用于計(jì)算平均值
scores += val # 使用外部函數(shù)的臨時(shí)變量
count += 1
return scores / count # 返回計(jì)算出的平均值
return inner_count_avg # 外部函數(shù)返回內(nèi)部函數(shù)引用
avg = get_avg()
print(avg(10)) # 報(bào)錯(cuò)
...

這里報(bào)錯(cuò)的原因,請(qǐng)看第 1 點(diǎn):變量的作用規(guī)則。因?yàn)?scores += val ,其實(shí)就是 scores = scores + val,有了賦值操作,則認(rèn)為 scores 是局部變量了。而我們也沒辦法使用 global 關(guān)鍵字,因?yàn)榇藭r(shí) scores 和 count 是定義在 get_ave 函數(shù)內(nèi)的,它們倆也是一個(gè)局部變量。而為什么我們使用 list 時(shí),沒有出現(xiàn)這個(gè)問(wèn)題呢?也是很好理解的,因?yàn)槲覀兪褂玫氖?list.append() 方法,它沒有賦值操作。你可以簡(jiǎn)單認(rèn)為,可變對(duì)象(即我們可以通過(guò)調(diào)用自身一些方法去做增刪改操作且變量地址值不變)不存在此問(wèn)題,而不可變對(duì)象則會(huì)有。

在 Python 3 中引入了一個(gè)關(guān)鍵詞 nonlocal 解決了這一個(gè)問(wèn)題:

# 一個(gè)簡(jiǎn)單的實(shí)現(xiàn)計(jì)算平均值的代碼改進(jìn)版二
def get_avg():
scores = 0 # 將外部臨時(shí)變量由 list 改為一個(gè) 整型數(shù)值
count = 0 # 同時(shí)新增一個(gè)變量,記錄個(gè)數(shù)
def inner_count_avg(val): # 內(nèi)部函數(shù),用于計(jì)算平均值
nonlocal count, scores
scores += val # 使用外部函數(shù)的臨時(shí)變量
count += 1
return scores / count # 返回計(jì)算出的平均值
return inner_count_avg # 外部函數(shù)返回內(nèi)部函數(shù)引用
avg = get_avg()
print(avg(10)) # 報(bào)錯(cuò)

你也許會(huì)說(shuō),那在 Python 2 的環(huán)境下應(yīng)該怎么解決呢?emm~其實(shí)呢 也是有辦法的,思路就是將不可變對(duì)象變?yōu)榭勺儗?duì)象即可。具體代碼如下:

# -*- coding:utf-8 -*-
class Score:
pass
def get_avg():
s = Score() # 使用類對(duì)象
s.scores = 0.0 # 注意 Python 2 中整數(shù)除法是舍棄小數(shù)的,所以要定義為浮點(diǎn)數(shù)
s.count = 0
def inner_get_avg(val):
s.count += 1
s.scores += val
return s.scores / s.count
return inner_get_avg
avg = get_avg()
print(avg(10)) # 10.0
print(avg(11)) # 10.5

4.閉包的應(yīng)用:首先是裝飾器,裝飾器就是通過(guò)修改被裝飾函數(shù),來(lái)達(dá)到增加新功能的作用。當(dāng)我們?cè)趦?nèi)部函數(shù)去修改被裝飾函數(shù)時(shí),大部分情況都會(huì)使用到閉包。簡(jiǎn)單示例:

def decorator(func): # 外部函數(shù)的局部變量 func
def wrapper(*args, **kwargs): # 接受被包裝函數(shù)傳入過(guò)來(lái)的參數(shù)
return func(*args, **kwargs) # 使用外部函數(shù)的局部變量 func
return wrapper
@decorator
def basic_func(name):
print 'my name is', name
# 等價(jià)于
decorator_func(func)

另外一個(gè)應(yīng)用由之前求平均值的示例也可以看出來(lái),可以在重復(fù)計(jì)算時(shí)提高效率。其次還有一個(gè)比較重要的應(yīng)用場(chǎng)景,就是利用“惰性求值”這一特性,這一點(diǎn)在 Django 的 QuerySet 里有體現(xiàn)。當(dāng)我們利用 ORM 去做 SQL 查詢時(shí),很多時(shí)候會(huì)根據(jù)不同的判斷條件,去加 filter,加 filter 的時(shí)候并沒有真正做查詢,在最終獲取結(jié)果的時(shí)候才真正執(zhí)行了查詢。

這里也是根據(jù)我們平常見到的一些Python常見面試題來(lái)跟大家講的Python閉包,有補(bǔ)充的伙伴非常歡迎留言補(bǔ)充哈!也希望大家能夠互相學(xué)習(xí),取長(zhǎng)補(bǔ)短,進(jìn)步呀!

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多