Python 可變型別作為函式預設引數時的副作用

2021-10-20 01:38:21 字數 2109 閱讀 8327

在 python 中定義函式時,可以為其指定預設引數,這樣就不必在每次呼叫函式時都傳遞引數進去,並且可以簡化我們的**。

在定義函式時,如果使用了可變型別來作為函式的預設引數,往往會產生一些***。來看下面一段**。

def

foo(li=

):1)

print

(li)

foo(

)foo(

)foo(

)

你可能想得到如下的結果:

[1]

[1][1]

但實際上,結果卻是:

[1]

[1, 1]

[1, 1, 1]

根據結果來看,似乎每次的函式呼叫,li列表都記錄了上一次的呼叫結果,而並沒有使用預設引數。但是當我們在每次呼叫函式時,都傳遞乙個空列表進去,就不會出現***,能夠正確得到我們想要的結果。

def

foo(li=

):1)

print

(li)

foo(

)foo(

)foo(

)

[1]

[1][1]

為什麼會產生這樣的結果呢?其實這個問題的原因是 python 中函式的特性所決定的。由於函式也是物件,而物件可以有自己的屬性,所以函式也有自己的屬性。

foo函式被建立時,它的預設引數就已經被儲存在它的__defaults__屬性中了。而函式只會被建立一次,以後每次執行foo()的時候,都只會呼叫函式,並不會去重新建立乙個函式。所以函式的預設引數也只會計算一次,無論之後被呼叫多少次,預設引數始終都是同乙個物件

我們用id()函式列印出缺省引數的記憶體位址就能夠看出來。

def

foo(li=

):1)

print

(li,

id(li)

)foo(

)foo(

)foo(

)

[1] 48904632

[1, 1] 48904632

[1, 1, 1] 48904632

可以看出,三次呼叫函式後,其內部列印的li位址是相同的,所以它們其實是同乙個物件

知道了問題的原因,那麼如果解決它呢?我們可以用none來作為函式的預設引數,在呼叫foo函式時,在函式體內部可以通過判斷li是否為none來決定是否需要使用預設值。這樣,當呼叫foo()函式並且沒有傳遞引數時,再給li賦乙個預設值即可。

def

foo(li=

none):

if li is

none

: li =

1)print

(li)

foo(

)foo(

)foo(

)

[1]

[1][1]

這樣既不用擔心可變型別作為函式預設引數時的***,也不用在每次呼叫foo函式時都傳遞乙個引數進去,能夠很好的解決這個問題。

所以,當我們給函式定義預設引數時,應該盡量使用不可變型別以免產生意想不到的***。當然,除非你明確知道你需要用可變型別的特性來達到某些目的。

Python函式之可變型別與不可變型別

在python語言中,string,tuple,number為不可變型別,但是dict,list為可變型別。所謂的不可變型別就是,a 5實際上指的是吧值為5的記憶體指向a,如果在執行a 10相當於又宣告了乙個值為10的記憶體指向a。例如 結果 兩次列印的a的記憶體位址是不同的。但是對於list的可變...

可變資料型別不能作為python函式的引數

可變資料型別 列表 字典 不可變資料型別 整型 浮點型 字串 元組 為什麼可變資料型別不能作為python函式的引數?請看以下例子 def foo a return aprint foo print foo print foo 結果 1 1,1 1,1,1 print id foo print id...

python的函式(三) 可變型別與不可變型別

python 中有下面幾種不可變型別 不可變型別 就是無法修改的型別,我們無法在記憶體中直接修改這個變數 如 100,student 如果我們嘗試對不可變型別進行修改,就會斷開原始的引用,重新分配記憶體位址。除了不可變型別,其餘的都是可變型別,如 可變型別 就是可以進行修改的型別,修改可變型別的值不...