本文將介紹使用mutable對(duì)象作為Python函數(shù)參數(shù)默認(rèn)值潛在的危害,以及其實(shí)現(xiàn)原理和設(shè)計(jì)目的
陷阱重現(xiàn)
我們就用實(shí)際的舉例來演示我們今天所要討論的主要內(nèi)容。
下面一段代碼定義了一個(gè)名為 generate_new_list_with 的函數(shù)。該函數(shù)的本意是在每次調(diào)用時(shí)都新建一個(gè)包含有給定 element 值的list。而實(shí)際運(yùn)行結(jié)果如下:
Python 2.7.9 (default, Dec 19 2014, 06:05:48)[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwinType "help", "copyright", "credits" or "license" for more information.>>> def generate_new_list_with(my_list=[], element=None):... my_list.append(element)... return my_list...>>> list_1 = generate_new_list_with(element=1)>>> list_1[1]>>> list_2 = generate_new_list_with(element=2)>>> list_2[1, 2]>>>
可見代碼運(yùn)行結(jié)果并不和我們預(yù)期的一樣。list_2在函數(shù)的第二次調(diào)用時(shí)并沒有得到一個(gè)新的list并填入2,而是在第一次調(diào)用結(jié)果的基礎(chǔ)上append了一個(gè)2。為什么會(huì)發(fā)生這樣在其他編程語言中簡(jiǎn)直就是設(shè)計(jì)bug一樣的問題呢?
準(zhǔn)備知識(shí):Python變量的實(shí)質(zhì)
要了解這個(gè)問題的原因我們先需要一個(gè)準(zhǔn)備知識(shí),那就是:Python變量到底是如何實(shí)現(xiàn)的?
Python變量區(qū)別于其他編程語言的申明&賦值方式,采用的是創(chuàng)建&指向的類似于指針的方式實(shí)現(xiàn)的。即Python中的變量實(shí)際上是對(duì)值或者對(duì)象的一個(gè)指針(簡(jiǎn)單的說他們是值得一個(gè)名字)。我們來看一個(gè)例子。
p = 1p = p+1
對(duì)于傳統(tǒng)語言,上面這段代碼的執(zhí)行方式將會(huì)是,先在內(nèi)存中申明一個(gè)p的變量,然后將1存入變量p所在內(nèi)存。執(zhí)行加法操作的時(shí)候得到2的結(jié)果,將2這個(gè)數(shù)值再次存入到p所在內(nèi)存地址中。可見整個(gè)執(zhí)行過程中,變化的是變量p所在內(nèi)存地址上的值
面這段代碼中,Python實(shí)際上是現(xiàn)在執(zhí)行內(nèi)存中創(chuàng)建了一個(gè)1的對(duì)象,并將p指向了它。在執(zhí)行加法操作的時(shí)候,實(shí)際上通過加法操作得到了一個(gè)2的新對(duì)象,并將p指向這個(gè)新的對(duì)象。可見整個(gè)執(zhí)行過程中,變化的是p指向的內(nèi)存地址
函數(shù)參數(shù)默認(rèn)值陷阱的根本原因
一句話來解釋:Python函數(shù)的參數(shù)默認(rèn)值,是在編譯階段就綁定的。
現(xiàn)在,我們先從一段摘錄來詳細(xì)分析這個(gè)陷阱的原因。下面是一段從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.
可見如果參數(shù)默認(rèn)值是在函數(shù)編譯compile階段就已經(jīng)被確定。之后所有的函數(shù)調(diào)用時(shí),如果參數(shù)不顯示的給予賦值,那么所謂的參數(shù)默認(rèn)值不過是一個(gè)指向那個(gè)在compile階段就已經(jīng)存在的對(duì)象的指針。如果調(diào)用函數(shù)時(shí),沒有顯示指定傳入?yún)?shù)值得話。那么所有這種情況下的該參數(shù)都會(huì)作為編譯時(shí)創(chuàng)建的那個(gè)對(duì)象的一種別名存在。
新聞熱點(diǎn)
疑難解答
圖片精選