Python中如何避免默認(rèn)參數(shù)的陷阱
在 Python 中,我們經(jīng)常會(huì)使用函數(shù)的默認(rèn)參數(shù)來(lái)簡(jiǎn)化代碼。但你知道嗎?默認(rèn)參數(shù)的行為有時(shí)可能會(huì)導(dǎo)致一些難以察覺(jué)的錯(cuò)誤,尤其是當(dāng)默認(rèn)參數(shù)是一個(gè)可變對(duì)象(如列表或字典)時(shí)。今天,我們就來(lái)探討一下這個(gè)問(wèn)題,并學(xué)習(xí)如何避免它。
問(wèn)題背景
我們通常會(huì)定義一個(gè)函數(shù),并為其提供一些默認(rèn)參數(shù)。例如:
def test(a, lst=[1, 2]): """ 給列表添加元素 :param a: 需要添加的元素 :param lst: 需要添加元素的列表 :return: 添加之后的列表 """ if a not in lst: lst.append(a) return lst print(test(4)) # [1, 2, 4] print(test(40)) # [1, 2, 40],實(shí)際輸出:[1, 2, 4, 40] print(test(40, lst=[])) # [40] print(test(80)) # 實(shí)際輸出:[1, 2, 4, 40, 80]
我們?cè)诙x函數(shù)的時(shí)候,創(chuàng)建的默認(rèn)參數(shù)lst,會(huì)在內(nèi)存中創(chuàng)建一塊存儲(chǔ)區(qū)域用來(lái)存放這個(gè)列表
當(dāng)我們第一次調(diào)用的時(shí)候,由于列表是可變類(lèi)型,我們修改的是實(shí)際的值,但是我們的映射地址沒(méi)有變
這個(gè)時(shí)候該地址對(duì)應(yīng)的數(shù)據(jù)就發(fā)生了變化
當(dāng)我們給這個(gè)lst傳值得時(shí)候,相當(dāng)于我們又新開(kāi)了一塊內(nèi)存,來(lái)儲(chǔ)存這個(gè)新的列表,這時(shí)候會(huì)生成一個(gè)新的地址
固當(dāng)我們?cè)俅问褂胻est(80)調(diào)用的時(shí)候,操作的仍是初始創(chuàng)建的列表
這個(gè)函數(shù)的目的是:接收一個(gè)參數(shù) a,然后將其添加到列表 lst 中。如果 a 不在列表中,就將它添加進(jìn)去并返回修改后的列表。
我們來(lái)看一下這個(gè)代碼的執(zhí)行過(guò)程。
默認(rèn)參數(shù)的行為
在 Python 中,函數(shù)的默認(rèn)參數(shù)值是 在函數(shù)定義時(shí)就計(jì)算并初始化的。這意味著,默認(rèn)參數(shù) lst=[1, 2] 只會(huì)在函數(shù)首次定義時(shí)創(chuàng)建一次,而不是每次調(diào)用函數(shù)時(shí)都會(huì)重新創(chuàng)建。如果你修改了默認(rèn)參數(shù)中的可變對(duì)象(比如列表、字典等),下次調(diào)用函數(shù)時(shí)這個(gè)對(duì)象會(huì)被“記住”。
示例分析
print(test(4)) # [1, 2, 4] print(test(40)) # [1, 2, 4, 40] print(test(40, lst=[])) # [40] print(test(80)) # [1, 2, 4, 40, 80]
第一行: 調(diào)用 test(4) 時(shí),lst 使用了默認(rèn)參數(shù) [1, 2],因此 4 被成功地添加到了列表中,返回 [1, 2, 4]。
第二行: 調(diào)用 test(40) 時(shí),由于默認(rèn)參數(shù) lst 被修改過(guò)了,變成了 [1, 2, 4],因此 40 被加入到了列表中,返回 [1, 2, 4, 40]。
第三行: 調(diào)用 test(40, lst=[]) 時(shí),你明確傳入了一個(gè)新的空列表 [],因此返回的是新的列表 [40],不會(huì)受到前面修改的影響。
第四行: 再次調(diào)用 test(80) 時(shí),lst 已經(jīng)被修改為 [1, 2, 4, 40],因此 80 被添加到列表中,最終返回 [1, 2, 4, 40, 80]。
問(wèn)題的根源
問(wèn)題的根源在于:默認(rèn)參數(shù)是共享的。當(dāng)你修改了默認(rèn)參數(shù)(如列表、字典等可變對(duì)象)時(shí),這些修改會(huì)影響到后續(xù)的函數(shù)調(diào)用。
在上面的代碼中,默認(rèn)列表 lst 是可變的,每次調(diào)用函數(shù)時(shí)都會(huì)操作同一個(gè)列表,從而導(dǎo)致了不可預(yù)期的結(jié)果。
如何避免默認(rèn)參數(shù)引發(fā)的問(wèn)題?
為了避免默認(rèn)參數(shù)引發(fā)的問(wèn)題,我們可以使用 None 作為默認(rèn)參數(shù),并在函數(shù)內(nèi)部檢查參數(shù)是否為 None,如果是,則創(chuàng)建一個(gè)新的列表。修改后的代碼如下:
def test(a, lst=None): if lst is None: lst = [1, 2] # 創(chuàng)建新的列表 if a not in lst: lst.append(a) return lst
這種做法能夠確保每次調(diào)用 test 函數(shù)時(shí),都使用一個(gè)全新的列表,而不會(huì)出現(xiàn)共享默認(rèn)參數(shù)的問(wèn)題。
總結(jié)
在 Python 中,默認(rèn)參數(shù)如果是可變對(duì)象(如列表、字典等),則它們的修改會(huì)影響到后續(xù)的調(diào)用。這種行為可能導(dǎo)致一些難以察覺(jué)的錯(cuò)誤。為了解決這個(gè)問(wèn)題,我們可以將默認(rèn)參數(shù)設(shè)置為 None,然后在函數(shù)內(nèi)部根據(jù)需要?jiǎng)?chuàng)建新的對(duì)象。這是避免共享可變對(duì)象的經(jīng)典做法。
到此這篇關(guān)于Python中如何避免默認(rèn)參數(shù)的陷阱的文章就介紹到這了,更多相關(guān)Python默認(rèn)參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PyQt5中多線(xiàn)程模塊QThread使用方法的實(shí)現(xiàn)
這篇文章主要介紹了PyQt5中多線(xiàn)程模塊QThread使用方法的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01python編程matplotlib交互繪制Julia集示例解析
matplotlib的Show面板中提供了放大、移動(dòng)等交互式操作,但也未能涵蓋所有的交互需求,比如希望通過(guò)mandelbrot集上的一點(diǎn)來(lái)生成對(duì)應(yīng)的Julia集2021-10-10Python數(shù)據(jù)分析入門(mén)之?dāng)?shù)據(jù)讀取與存儲(chǔ)
今天繼續(xù)帶大家學(xué)習(xí)python數(shù)據(jù)分析,下文中有非常詳細(xì)的代碼示例,清楚地解釋了python數(shù)據(jù)讀取與存儲(chǔ)的相關(guān)知識(shí),需要的朋友可以參考下2021-05-05python 實(shí)現(xiàn)UTC時(shí)間加減的方法
今天小編就為大家分享一篇python 實(shí)現(xiàn)UTC時(shí)間加減的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12使用PyQt5編寫(xiě)一個(gè)簡(jiǎn)單的取色器
這篇文章主要為大家介紹了PyQt5搭建的一個(gè)取色器,一共寫(xiě)了兩款應(yīng)用,一款使用快捷鍵捕獲鼠標(biāo)附近圖像的RGB和16進(jìn)制顏色編碼,一款跟隨鼠標(biāo)刷新圖像的RGB和16進(jìn)制顏色編碼,希望對(duì)大家有所幫助2025-01-01Python+matplotlib+numpy實(shí)現(xiàn)在不同平面的二維條形圖
這篇文章主要介紹了Python+matplotlib+numpy實(shí)現(xiàn)在不同平面的二維條形圖,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01深入解析Python編程中super關(guān)鍵字的用法
Python的子類(lèi)調(diào)用父類(lèi)成員時(shí)可以用到super關(guān)鍵字,初始化時(shí)需要注意super()和__init__()的區(qū)別,下面我們就來(lái)深入解析Python編程中super關(guān)鍵字的用法:2016-06-06pygame實(shí)現(xiàn)俄羅斯方塊游戲(AI篇2)
這篇文章主要為大家詳細(xì)介紹了pygame實(shí)現(xiàn)俄羅斯方塊游戲AI的第2篇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10python enumerate函數(shù)的使用方法總結(jié)
這篇文章主要介紹了python enumerate使用方法總結(jié),enumerate函數(shù)用于遍歷序列中的元素以及它們的下標(biāo),有興趣的可以了解一下2017-11-11