一文帶你了解Python中的延遲綁定
延遲綁定是什么?
Python中的延遲綁定是指在嵌套函數(shù)中,內(nèi)部函數(shù)在被調(diào)用時(shí)才會(huì)綁定外部函數(shù)的變量,而不是在定義內(nèi)部函數(shù)時(shí)就綁定。這種綁定方式可以導(dǎo)致一些出乎意料的行為,因?yàn)樽兞康闹凳窃诤瘮?shù)調(diào)用時(shí)決定的,而不是在函數(shù)定義時(shí)。
具體來(lái)說(shuō),當(dāng)一個(gè)嵌套函數(shù)引用了外部函數(shù)的變量時(shí),Python會(huì)在內(nèi)部函數(shù)被調(diào)用時(shí)搜索變量的值,而不是在內(nèi)部函數(shù)定義時(shí)。這意味著如果外部函數(shù)的變量在內(nèi)部函數(shù)被調(diào)用之前被改變了,內(nèi)部函數(shù)將使用新的變量值,而不是定義時(shí)的值。這種行為可能會(huì)導(dǎo)致一些困惑和錯(cuò)誤,特別是在使用嵌套函數(shù)進(jìn)行編程時(shí)。
舉個(gè)栗子
下面是一個(gè)例子,展示了延遲綁定的行為:
def outer():
numbers = [1, 2, 3, 4, 5]
funcs = []
for number in numbers:
def inner():
return number
funcs.append(inner)
return funcs
for func in outer():
print(func())輸出結(jié)果為:
5
5
5
5
5
這是因?yàn)槊總€(gè)內(nèi)部函數(shù)都引用了外部函數(shù)的 number 變量,但是這個(gè)變量在內(nèi)部函數(shù)被調(diào)用時(shí)才會(huì)被綁定。由于 number 在每個(gè)迭代中的值都被重新賦值,所有內(nèi)部函數(shù)都返回最后一個(gè)值,即 5。
為了避免延遲綁定可能導(dǎo)致的問(wèn)題,可以通過(guò)將變量的值作為參數(shù)傳遞給內(nèi)部函數(shù)來(lái)顯式地綁定變量。例如,上面的代碼可以修改如下:
def outer():
numbers = [1, 2, 3, 4, 5]
funcs = []
for number in numbers:
def inner(number=number):
return number
funcs.append(inner)
return funcs
for func in outer():
print(func())輸出結(jié)果為:
1
2
3
4
5
在這個(gè)版本中,每個(gè)內(nèi)部函數(shù)都有一個(gè)默認(rèn)參數(shù) number,它的默認(rèn)值是外部循環(huán)的 number 變量。由于默認(rèn)參數(shù)的值在內(nèi)部函數(shù)被定義時(shí)就被確定了,所以每個(gè)內(nèi)部函數(shù)都綁定了不同的變量值。
另一個(gè)典型的栗子
def multipliers():
return [lambda x : i*x for i in range(4)]
print([m(2) for m in multipliers()])輸出結(jié)果為:
[6, 6, 6, 6]
是不是和你的想不一樣呢!!為什么呢??
這是因?yàn)椋?code>multipliers函數(shù)中,返回的是一個(gè)包含四個(gè) lambda 函數(shù)的列表,這些 lambda 函數(shù)的形式參數(shù)為 x,函數(shù)體為 i*x。當(dāng)這些 lambda 函數(shù)被調(diào)用時(shí),它們的 i 取決于它們?cè)诹斜碇械乃饕?,而不是在定義時(shí)的值。因此,當(dāng)我們?cè)?[m(2) for m in multipliers()] 中迭代這些 lambda 函數(shù)并傳遞 2 作為參數(shù)時(shí),所有 lambda 函數(shù)的 i 都是最后一個(gè) i 的值,即 3,因此所有的 lambda 函數(shù)都會(huì)返回 3*2=6。
還不是很清楚?
沒(méi)關(guān)系,讓我們換一種方式解釋下。
將 lambda 函數(shù)轉(zhuǎn)換為等價(jià)的普通函數(shù),可以更清晰地看到問(wèn)題出在哪里。 首先,我們將原始的 lambda 函數(shù):
lambda x : i*x
轉(zhuǎn)換為等價(jià)的普通函數(shù):
def multiplier(x):
return i*x然后,我們將 multipliers() 函數(shù)中的 lambda 函數(shù)列表轉(zhuǎn)換為等價(jià)的普通函數(shù)列表:
def multipliers():
funcs = []
for i in range(4):
def multiplier(x):
return i*x
funcs.append(multiplier)
return funcs現(xiàn)在,我們可以更清晰地看到問(wèn)題出在哪里。在原始的 lambda 函數(shù)中,i 是一個(gè)自由變量,它的值在函數(shù)調(diào)用時(shí)動(dòng)態(tài)綁定。但是,在 multipliers() 函數(shù)中,每個(gè) multiplier() 函數(shù)都使用了同一個(gè)自由變量 i,其值在函數(shù)迭代結(jié)束后被設(shè)置為 3。因此,當(dāng)我們迭代這些函數(shù)并傳遞 2 作為參數(shù)時(shí),每個(gè)函數(shù)都會(huì)乘以最后一個(gè) i 的值,也就是 3,所以結(jié)果會(huì)是 [6, 6, 6, 6]。
如果要解決這個(gè)問(wèn)題,可以使用閉包來(lái)捕獲每個(gè) lambda 函數(shù)所需的 i 值,使每個(gè)函數(shù)都有自己獨(dú)立的 i 值。這樣,當(dāng)我們迭代這些函數(shù)并傳遞參數(shù) 2 時(shí),每個(gè)函數(shù)都會(huì)乘以它們自己獨(dú)立的 i 值,而不是最后一個(gè) i 的值。
怎么避免這個(gè)問(wèn)題呢
要避免這個(gè)問(wèn)題,我們可以將 lambda 函數(shù)中的 i 變?yōu)槟J(rèn)參數(shù),這樣每個(gè) lambda 函數(shù)都會(huì)有一個(gè)獨(dú)立的 i 值。下面是一個(gè)修改后的代碼:
def multipliers():
return [lambda x, i=i : i*x for i in range(4)]
print([m(2) for m in multipliers()])輸出結(jié)果為:
[0, 2, 4, 6]
現(xiàn)在,每個(gè) lambda 函數(shù)都有一個(gè)獨(dú)立的 i 值,因此輸出結(jié)果正確。
將 lambda 函數(shù)轉(zhuǎn)換為等價(jià)的普通函數(shù),可以更清晰地看到問(wèn)題出在哪里。
def multipliers():
funcs = []
for i in range(4):
def multiplier(x, i=i):
return i*x
funcs.append(multiplier)
return funcs這里我們使用了閉包來(lái)捕獲每個(gè) lambda 函數(shù)所需的 i 值,這樣每個(gè)函數(shù)都有一個(gè)獨(dú)立的 i 值。
現(xiàn)在,我們可以更清晰地看到問(wèn)題出在哪里。在原始的 lambda 函數(shù)中,i 是一個(gè)自由變量,它的值在函數(shù)調(diào)用時(shí)動(dòng)態(tài)綁定。但是,在 multipliers() 函數(shù)中,每個(gè) multiplier() 函數(shù)都有自己獨(dú)立的 i 值,這個(gè)值是在函數(shù)定義時(shí)靜態(tài)綁定的。因此,當(dāng)我們迭代這些函數(shù)并傳遞 2 作為參數(shù)時(shí),所有函數(shù)的 i 值都是它們?cè)诙x時(shí)的值,而不是在調(diào)用時(shí)動(dòng)態(tài)綁定的值。
通過(guò)使用閉包來(lái)捕獲每個(gè) lambda 函數(shù)所需的 i 值,我們可以解決這個(gè)問(wèn)題,使每個(gè)函數(shù)都有自己獨(dú)立的 i 值。
到此這篇關(guān)于一文帶你了解Python中的延遲綁定的文章就介紹到這了,更多相關(guān)Python延遲綁定內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用Jmeter實(shí)現(xiàn)在請(qǐng)求param和body里面加入隨機(jī)參數(shù)
本文介紹了如何使用jemeter實(shí)現(xiàn)新增接口壓力測(cè)試中的隨機(jī)參數(shù)生成,首先,使用函數(shù)助手對(duì)話框生成隨機(jī)數(shù),然后將生成的隨機(jī)數(shù)作為參數(shù)放入請(qǐng)求中,無(wú)論請(qǐng)求格式是json、xml還是text等,如果param和body同時(shí)存在并需要隨機(jī)生成參數(shù),可以把參數(shù)寫入到請(qǐng)求地址中2024-10-10
Python實(shí)現(xiàn)K-近鄰算法的示例代碼
k-近鄰算法(K-Nearest Neighbour algorithm),又稱 KNN 算法,是數(shù)據(jù)挖掘技術(shù)中原理最簡(jiǎn)單的算法。本文將介紹實(shí)現(xiàn)K-近鄰算法的示例代碼,需要的可以參考一下2022-09-09
Python機(jī)器學(xué)習(xí)應(yīng)用之基于BP神經(jīng)網(wǎng)絡(luò)的預(yù)測(cè)篇詳解
BP(back?propagation)神經(jīng)網(wǎng)絡(luò)是1986年由Rumelhart和McClelland為首的科學(xué)家提出的概念,是一種按照誤差逆向傳播算法訓(xùn)練的多層前饋神經(jīng)網(wǎng)絡(luò),是應(yīng)用最廣泛的神經(jīng)網(wǎng)絡(luò)模型之一2022-01-01
利用Python實(shí)現(xiàn)面部識(shí)別的方法詳解
人臉識(shí)別正在成為軟件開(kāi)發(fā)中的一種趨勢(shì)。它有助于識(shí)別人臉并使應(yīng)用程序更加健壯。本文將使用python和face_recognition庫(kù)創(chuàng)建一個(gè)簡(jiǎn)單的人臉識(shí)別,需要的可以參考一下2022-05-05
python 反編譯exe文件為py文件的實(shí)例代碼
這篇文章主要介紹了python 反編譯exe文件為py文件的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06
Python phone模塊獲取手機(jī)號(hào)歸屬地 區(qū)號(hào) 運(yùn)營(yíng)商等信息demo
這篇文章主要介紹了Python phone模塊獲取手機(jī)號(hào)歸屬地 區(qū)號(hào) 運(yùn)營(yíng)商等信息的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
利用Python實(shí)現(xiàn)Windows定時(shí)關(guān)機(jī)功能
是最初的幾個(gè)爬蟲(chóng),讓我認(rèn)識(shí)了Python這個(gè)新朋友,雖然才剛認(rèn)識(shí)了幾天,但感覺(jué)有種莫名的默契感。下面通過(guò)這篇文章給大家介紹Python實(shí)現(xiàn)Windows定時(shí)關(guān)機(jī)功能,需要的朋友可以參考下2017-03-03
用Python實(shí)現(xiàn)斐波那契(Fibonacci)函數(shù)
這篇文章主要介紹了用Python實(shí)現(xiàn)斐波那契(Fibonacci)函數(shù)的相關(guān)資料,需要的朋友可以參考下2016-03-03
python實(shí)現(xiàn)鬧鐘定時(shí)播放音樂(lè)功能
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)鬧鐘定時(shí)播放音樂(lè)功能,播放wav格式的音頻,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01

