一文帶你了解Python中的延遲綁定
延遲綁定是什么?
Python中的延遲綁定是指在嵌套函數(shù)中,內(nèi)部函數(shù)在被調(diào)用時才會綁定外部函數(shù)的變量,而不是在定義內(nèi)部函數(shù)時就綁定。這種綁定方式可以導(dǎo)致一些出乎意料的行為,因?yàn)樽兞康闹凳窃诤瘮?shù)調(diào)用時決定的,而不是在函數(shù)定義時。
具體來說,當(dāng)一個嵌套函數(shù)引用了外部函數(shù)的變量時,Python會在內(nèi)部函數(shù)被調(diào)用時搜索變量的值,而不是在內(nèi)部函數(shù)定義時。這意味著如果外部函數(shù)的變量在內(nèi)部函數(shù)被調(diào)用之前被改變了,內(nèi)部函數(shù)將使用新的變量值,而不是定義時的值。這種行為可能會導(dǎo)致一些困惑和錯誤,特別是在使用嵌套函數(shù)進(jìn)行編程時。
舉個栗子
下面是一個例子,展示了延遲綁定的行為:
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)槊總€內(nèi)部函數(shù)都引用了外部函數(shù)的 number
變量,但是這個變量在內(nèi)部函數(shù)被調(diào)用時才會被綁定。由于 number
在每個迭代中的值都被重新賦值,所有內(nèi)部函數(shù)都返回最后一個值,即 5。
為了避免延遲綁定可能導(dǎo)致的問題,可以通過將變量的值作為參數(shù)傳遞給內(nèi)部函數(shù)來顯式地綁定變量。例如,上面的代碼可以修改如下:
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
在這個版本中,每個內(nèi)部函數(shù)都有一個默認(rèn)參數(shù) number
,它的默認(rèn)值是外部循環(huán)的 number
變量。由于默認(rèn)參數(shù)的值在內(nèi)部函數(shù)被定義時就被確定了,所以每個內(nèi)部函數(shù)都綁定了不同的變量值。
另一個典型的栗子
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ù)中,返回的是一個包含四個 lambda 函數(shù)的列表,這些 lambda 函數(shù)的形式參數(shù)為 x
,函數(shù)體為 i*x
。當(dāng)這些 lambda 函數(shù)被調(diào)用時,它們的 i
取決于它們在列表中的索引,而不是在定義時的值。因此,當(dāng)我們在 [m(2) for m in multipliers()]
中迭代這些 lambda 函數(shù)并傳遞 2
作為參數(shù)時,所有 lambda 函數(shù)的 i
都是最后一個 i
的值,即 3
,因此所有的 lambda 函數(shù)都會返回 3*2=6
。
還不是很清楚?
沒關(guān)系,讓我們換一種方式解釋下。
將 lambda 函數(shù)轉(zhuǎn)換為等價的普通函數(shù),可以更清晰地看到問題出在哪里。 首先,我們將原始的 lambda 函數(shù):
lambda x : i*x
轉(zhuǎn)換為等價的普通函數(shù):
def multiplier(x): return i*x
然后,我們將 multipliers()
函數(shù)中的 lambda 函數(shù)列表轉(zhuǎn)換為等價的普通函數(shù)列表:
def multipliers(): funcs = [] for i in range(4): def multiplier(x): return i*x funcs.append(multiplier) return funcs
現(xiàn)在,我們可以更清晰地看到問題出在哪里。在原始的 lambda 函數(shù)中,i
是一個自由變量,它的值在函數(shù)調(diào)用時動態(tài)綁定。但是,在 multipliers()
函數(shù)中,每個 multiplier()
函數(shù)都使用了同一個自由變量 i
,其值在函數(shù)迭代結(jié)束后被設(shè)置為 3
。因此,當(dāng)我們迭代這些函數(shù)并傳遞 2
作為參數(shù)時,每個函數(shù)都會乘以最后一個 i
的值,也就是 3
,所以結(jié)果會是 [6, 6, 6, 6]
。
如果要解決這個問題,可以使用閉包來捕獲每個 lambda 函數(shù)所需的 i
值,使每個函數(shù)都有自己獨(dú)立的 i
值。這樣,當(dāng)我們迭代這些函數(shù)并傳遞參數(shù) 2
時,每個函數(shù)都會乘以它們自己獨(dú)立的 i
值,而不是最后一個 i
的值。
怎么避免這個問題呢
要避免這個問題,我們可以將 lambda 函數(shù)中的 i
變?yōu)槟J(rèn)參數(shù),這樣每個 lambda 函數(shù)都會有一個獨(dú)立的 i
值。下面是一個修改后的代碼:
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)在,每個 lambda 函數(shù)都有一個獨(dú)立的 i
值,因此輸出結(jié)果正確。
將 lambda 函數(shù)轉(zhuǎn)換為等價的普通函數(shù),可以更清晰地看到問題出在哪里。
def multipliers(): funcs = [] for i in range(4): def multiplier(x, i=i): return i*x funcs.append(multiplier) return funcs
這里我們使用了閉包來捕獲每個 lambda 函數(shù)所需的 i
值,這樣每個函數(shù)都有一個獨(dú)立的 i
值。
現(xiàn)在,我們可以更清晰地看到問題出在哪里。在原始的 lambda 函數(shù)中,i
是一個自由變量,它的值在函數(shù)調(diào)用時動態(tài)綁定。但是,在 multipliers()
函數(shù)中,每個 multiplier()
函數(shù)都有自己獨(dú)立的 i
值,這個值是在函數(shù)定義時靜態(tài)綁定的。因此,當(dāng)我們迭代這些函數(shù)并傳遞 2
作為參數(shù)時,所有函數(shù)的 i
值都是它們在定義時的值,而不是在調(diào)用時動態(tài)綁定的值。
通過使用閉包來捕獲每個 lambda 函數(shù)所需的 i
值,我們可以解決這個問題,使每個函數(shù)都有自己獨(dú)立的 i
值。
到此這篇關(guān)于一文帶你了解Python中的延遲綁定的文章就介紹到這了,更多相關(guān)Python延遲綁定內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在ironpython中利用裝飾器執(zhí)行SQL操作的例子
這篇文章主要介紹了在ironpython中利用裝飾器執(zhí)行SQL操作的例子,文章中以操作MySQL為例,需要的朋友可以參考下2015-05-05Python基于opencv調(diào)用攝像頭獲取個人圖片的實(shí)現(xiàn)方法
今天小編就為大家分享一篇關(guān)于Python基于opencv調(diào)用攝像頭獲取個人圖片的實(shí)現(xiàn)方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02淺談pandas中shift和diff函數(shù)關(guān)系
下面小編就為大家分享一篇淺談pandas中shift和diff函數(shù)關(guān)系,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04Python3.6 + TensorFlow 安裝配置圖文教程(Windows 64 bit)
這篇文章主要介紹了Python3.6 + TensorFlow 安裝配置的教程(Windows 64 bit),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02Anaconda下Python中GDAL模塊的下載與安裝過程
這篇文章主要介紹了Anaconda下Python中GDAL模塊的下載與安裝方法,本文介紹在Anaconda環(huán)境下,安裝Python中柵格、矢量等地理數(shù)據(jù)處理庫GDAL的方法,需要的朋友可以參考下2023-04-04在Python中關(guān)于使用os模塊遍歷目錄的實(shí)現(xiàn)方法
今天小編就為大家分享一篇在Python中關(guān)于使用os模塊遍歷目錄的實(shí)現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-01-01150行python代碼實(shí)現(xiàn)貪吃蛇游戲
這篇文章主要為大家詳細(xì)介紹了150行python代碼實(shí)現(xiàn)貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-04-04用python實(shí)現(xiàn)域名資產(chǎn)監(jiān)控的詳細(xì)步驟
域名資產(chǎn)監(jiān)控,通過輸入一個主域名,找到該域名對應(yīng)的ip地址所在的服務(wù)器的端口開閉情況,本文重點(diǎn)給大家介紹用python實(shí)現(xiàn)域名資產(chǎn)監(jiān)控的問題,需要的朋友可以參考下2021-11-11