把陷阱去掉了,反倒踩進(jìn)了新的陷阱?
相信很多人都知道,Python有一個(gè)默認(rèn)參數(shù)陷阱。函數(shù)的默認(rèn)參數(shù)不能使用可變類(lèi)型,否則會(huì)導(dǎo)致運(yùn)行結(jié)果跟你想的不一樣。例如:
這段代碼運(yùn)行的時(shí)候,如果傳入了一個(gè)列表,那么就往列表里面添加青南和產(chǎn)品經(jīng)理?并用逗號(hào)連接起來(lái)打印。如果沒(méi)有傳入?yún)?shù),就打印青南,產(chǎn)品經(jīng)理。看起來(lái)似乎沒(méi)有問(wèn)題。但如果你不帶參數(shù)多運(yùn)行幾次,就會(huì)發(fā)現(xiàn)問(wèn)題出來(lái)了:
為什么每次不傳入?yún)?shù)的時(shí)候,打印的結(jié)果都不一樣?而且越來(lái)越長(zhǎng)?這個(gè)原因我公眾號(hào)以前已經(jīng)講過(guò)了,根本原因就在于默認(rèn)參數(shù)user_list=[]?這里的默認(rèn)值[]是在代碼運(yùn)行時(shí)(Runtime)啟動(dòng)的時(shí)候就初始化的,每次調(diào)用函數(shù)一直使用這同一個(gè)對(duì)象,并不是每次調(diào)用函數(shù)的時(shí)候初始化。
要解決這個(gè)問(wèn)題也非常簡(jiǎn)單,默認(rèn)參數(shù)使用不可變對(duì)象就可以了:
最近,我在上古代碼中開(kāi)發(fā)新功能,看到有一段處理Exception的函數(shù),默認(rèn)參數(shù)就使用的字典。代碼大概長(zhǎng)成下面這樣:
于是我就順手把它改了:
理論上講,我這樣改移除了一個(gè)隱患,并且對(duì)后面的具體代碼來(lái)說(shuō),param_dict始終都是一個(gè)字典,應(yīng)該沒(méi)有什么問(wèn)題才對(duì)。
結(jié)果不久以后,有人給我報(bào)Bug。我一看,不就是我改的這個(gè)函數(shù)報(bào)錯(cuò)了嗎。一通分析函數(shù)調(diào)用棧,發(fā)現(xiàn)了問(wèn)題的原因。
這個(gè)函數(shù)原來(lái)是這樣寫(xiě)的:
而上古代碼里面,調(diào)用這個(gè)函數(shù)的時(shí)候,有下面兩種寫(xiě)法:
當(dāng)他用不到param_dict?參數(shù)的時(shí)候,他竟然主動(dòng)傳了個(gè)None?進(jìn)去。這樣一來(lái),他傳入的None?就會(huì)被我強(qiáng)制轉(zhuǎn)換為空字典。于是代碼就會(huì)走到extra_msg.format(**param_dict)里面。這個(gè)時(shí)候由于沒(méi)有填充大括號(hào)中的參數(shù),于是就報(bào)錯(cuò)了:
這個(gè)新的bug解決起來(lái)也簡(jiǎn)單,再判斷一下param_dict是不是空就可以了:
這真的應(yīng)驗(yàn)了那句話,當(dāng)一段顯然有問(wèn)題的代碼竟然正常運(yùn)行的時(shí)候,你就不要去動(dòng)他了,它可能處于負(fù)負(fù)得正的狀態(tài),這一改反而可能把它改錯(cuò)了。