Python函數(shù)的默認(rèn)參數(shù)設(shè)計(jì)示例詳解
在Python教程里,針對默認(rèn)參數(shù),給了一個(gè)“重要警告”的例子:
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3))
默認(rèn)值只會執(zhí)行一次,也沒說原因。會打印出結(jié)果:
[1]
[1, 2]
[1, 2, 3]
因?yàn)閷W(xué)的第一門語言是Ruby,所以感覺有些奇怪。 但肯定的是方法f一定儲存了變量L。
準(zhǔn)備知識:指針
p指向不可變對象,比如數(shù)字。則相當(dāng)于p指針指向了不同的內(nèi)存地址。
p指向的是可變對象,比如list。list自身的改變,并不會改變list對象自身所在的內(nèi)存地址。所以p指向的內(nèi)存地址不變。
>>> p = 1 >>> id(p) >>> p = p + 1 >>> id(p) >>> p = 11 >>> id(p) >>> p = [] >>> id(p) >>> p.append(11) >>> id(p)
根本原因
Python函數(shù)的參數(shù)默認(rèn)值,是在編譯階段就綁定了。(寫代碼時(shí)就定義了。)
下面是一段從Python Common Gotchas中摘錄的原因解釋:
Python's default arguments are evaluated once when the function is defined, not each time the function is called (like it is in say, Ruby). This means that if you use a mutable default argument and mutate it, you will and have mutated that object for all future calls to the function as well.
由此可知:
- 在運(yùn)行代碼時(shí),運(yùn)行到函數(shù)定義時(shí),默認(rèn)參數(shù)的表達(dá)式就被執(zhí)行了。
- 函數(shù)調(diào)用時(shí),不會再次運(yùn)行默認(rèn)參數(shù)的表達(dá)式。⚠️ 這點(diǎn)和Ruby完全不同。
- 由此可知,如果默認(rèn)參數(shù),指向一個(gè)不變對象,例如L = 1。那么在函數(shù)調(diào)用時(shí),在函數(shù)體內(nèi)對L重新賦值,L其實(shí)是一個(gè)新的指針, 指向的是一個(gè)新的內(nèi)存地址。而原來默認(rèn)參數(shù)L本身及指向的內(nèi)存地址,已經(jīng)儲存在最開始編譯時(shí)的函數(shù)定義中??梢杂胈_default__查看。
- 如果默認(rèn)參數(shù)指向的是一個(gè)可變對象,如list, 那么L.append(a)是對可變對象自身的修改,L指向的內(nèi)存地址不變。所以每次調(diào)用函數(shù),默認(rèn)參數(shù)取出的都是這個(gè)內(nèi)存地址的對象。
第三條,修改上面的例子:
def f(a, L = 1):
L = a
print(id(L))
return L
print("self",id(f.__defaults__[0]))
print(f(1))
print("self",id(f.__defaults__[0]))
print(f(33))
print("self",id(f.__defaults__[0]))
#運(yùn)行結(jié)果:
self 4353170064
1
self 4353170064
33
self 4353170064
默認(rèn)參數(shù)L,在編譯階段就綁定了,儲存在__default__內(nèi)。函數(shù)體內(nèi)的L = a表達(dá)式,生成的是新的變量。返回的L是新的變量,和默認(rèn)參數(shù)無關(guān)。
第四條,還是上面的例子, 改一下默認(rèn)參數(shù)的類型為可變對象list:
def f(a, L = []):
L.append(a)
print(id(L))
return L
# L = f(1)
print("self",id(f.__defaults__[0]))
print(f(1))
print("self",id(f.__defaults__[0]))
print(f(33))
print("self",id(f.__defaults__[0]))
#返回結(jié)果 self 4337586048 [1] self 4337586048 [1, 33] self 4337586048
由id號可知,返回的是默認(rèn)參數(shù)自身。
如何避免這個(gè)陷阱帶來不必要麻煩
def f(a, L = None):
if L is None:
L = []
L.append(a)
return L
為什么Python要這么設(shè)計(jì)
StackOverflow 上爭論很多。
Ruby之所以沒有這個(gè)問題,我想是因?yàn)镽uby的def關(guān)鍵字定義了一個(gè)封閉作用域,任何數(shù)據(jù)都必須通過參數(shù)傳入到方法內(nèi),才能用。
和Ruby比,Python參數(shù)的作用被大大消弱了。Python的出現(xiàn)晚于Ruby,其創(chuàng)始人肯定參考了Ruby的設(shè)計(jì)。拋棄了這個(gè)設(shè)計(jì),選擇了類似javascript的函數(shù)方式。def定義的函數(shù),執(zhí)行時(shí)是可以接收外部作用域的變量的。
有觀點(diǎn)認(rèn)為:
出于Python編譯器的實(shí)現(xiàn)方式考慮,函數(shù)是一個(gè)內(nèi)部一級對象。而參數(shù)默認(rèn)值是這個(gè)對象的屬性。在其他任何語言中,對象屬性都是在對象創(chuàng)建時(shí)做綁定的。因此,函數(shù)參數(shù)默認(rèn)值在編譯時(shí)綁定也就不足為奇了。
本文參考了:http://cenalulu.github.io/python/default-mutable-arguments/#toc1 ,并加入了自己的理解。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。
- python函數(shù)默認(rèn)參數(shù)使用避坑指南
- python函數(shù)的默認(rèn)參數(shù)請勿定義可變類型詳解
- Python中的函數(shù)參數(shù)(位置參數(shù)、默認(rèn)參數(shù)、可變參數(shù))
- Python如何定義有默認(rèn)參數(shù)的函數(shù)
- Python新手學(xué)習(xí)函數(shù)默認(rèn)參數(shù)設(shè)置
- Python函數(shù)默認(rèn)參數(shù)常見問題及解決方案
- Python中函數(shù)及默認(rèn)參數(shù)的定義與調(diào)用操作實(shí)例分析
- Python進(jìn)階-函數(shù)默認(rèn)參數(shù)(詳解)
- 深入講解Python函數(shù)中參數(shù)的使用及默認(rèn)參數(shù)的陷阱
- 詳細(xì)介紹Python函數(shù)中的默認(rèn)參數(shù)
- Python函數(shù)默認(rèn)參數(shù)設(shè)置的具體方法
相關(guān)文章
python基礎(chǔ)pandas的drop()用法示例詳解
這篇文章主要介紹了python基礎(chǔ)pandas的drop()用法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
python中的scapy抓取http報(bào)文內(nèi)容
這篇文章主要介紹了python中的scapy抓取http報(bào)文內(nèi)容方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Windows自動化Python?pyautogui?RPA操作實(shí)現(xiàn)
本文詳細(xì)介紹了使用Python的pyautogui庫進(jìn)行Windows自動化操作的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
結(jié)合Python的SimpleHTTPServer源碼來解析socket通信
SimpleHTTPServer是Python中一個(gè)現(xiàn)成的HTTP服務(wù)器例子,本文我們將結(jié)合Python的SimpleHTTPServer源碼來解析socket通信,我們先來看一下socket的基本概念:2016-06-06
用Python+OpenCV對比圖像質(zhì)量的幾種方法
這篇文章主要介紹了用Python+OpenCV對比圖像質(zhì)量過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
django admin添加數(shù)據(jù)自動記錄user到表中的實(shí)現(xiàn)方法
下面小編就為大家分享一篇django admin添加數(shù)據(jù)自動記錄user到表中的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01
詳解Python常用標(biāo)準(zhǔn)庫之os模塊與shutil模塊
os系統(tǒng)模塊與shutil文件操作模塊是Python常用的標(biāo)準(zhǔn)庫,本文將通過示例詳細(xì)講解一下二者的使用,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-06-06
Python實(shí)戰(zhàn)之疫苗研發(fā)情況可視化
2020年底以來,歐美,印度,中國,俄羅斯等多國得制藥公司紛紛推出了針對新冠/肺炎的疫苗,這部分主要分析了2020年以來全球疫情形勢,各類疫苗在全球的地理分布,疫苗在各國的接種進(jìn)度進(jìn)行可視化展示,需要的朋友可以參考下2021-05-05

