首頁 > 軟體

python函數的預設引數請勿定義可變型別詳解

2022-02-11 16:00:20

函數的預設引數請勿定義可變型別

經常會看到這樣一句程式碼警告:

Default argument value is mutable

意思是告訴我們函數的定義中,使用可變型別做預設引數。

那為什麼會有這個警告呢?

可變型別和不可變型別

  • 可變型別(mutable):列表,字典
  • 不可變型別(unmutable):數位,字串,元組

定義可變型別會有什麼問題?

def fun(a=[]):
    a.append(1)
    print(a)
if __name__ == "__main__":
    fun()
    fun()
>>> [1]
    [1, 1]

可以發現,預設引數定義可變型別之後,在第二次乃至更多次地呼叫同一個函數時,預設引數彷彿失去了效果。

此時,在需要重複呼叫同一個函數的場景中,就非常容易導致問題,並且該問題不易察覺。在debug的時候就會表現成明明沒有引數傳進來,但是函數引數會有值,並且執行了不應該執行的操作。

導致的原因

我的理解:

我們定義的函數本身是一個function的範例化物件,每當我們進行函數的定義時,就是建立了一個function的範例化物件,而預設引數就是其屬性。

在沒有傳入引數,以預設引數形式呼叫,並且改變了函數物件的屬性值時,改變的屬性值被儲存下來,當第二次呼叫同一個物件時,屬性值已經發生了改變。

type(fun)
>>> function

解決方法

def fun(a=None):
    if a is None:
    a = []
    a.append(1)
    print(a)
if __name__ == "__main__":
    fun()
    fun()
>>> [1]
    [1]

關於可變型別作為預設引數時的注意點

請先看程式碼,看看程式碼的輸出是否和你想的一樣。

def e(v,l=[]):
    l.append(v)
    return l
l1=e(10)
l2=e(123,[])
l3=e("a")
print(l1,l2,l3)
# 輸出: ([10, 'a'], [123], [10, 'a'])

關於上述程式碼,標準解釋是:帶有預設引數的表示式在函數被定義的時候被計算,不是在呼叫的時候計算。

我覺得通俗的解釋是:當不傳預設值的時候,無論呼叫多少次該函數,在函數體內部使用的一直都是那個預設的“l”,而這個預設的“l”又是可變型別,所以,它的改變會影響所有指向它的變數,也就是l1和l3。

為了使以上兩點的觀點更加站的住腳,我進行以下幾個測試。

測試:將可變型別列表換為字典

def e(k,v,d={}):
    d[k]=v
    return d
d1=e(10,10)
d2=e(123,123,{})
d3=e("a","a")
print(d1,d2,d3)
# 輸出:({'a': 'a', 10: 10}, {123: 123}, {'a': 'a', 10: 10})

測試:來個不可變型別字串

def e(v,s=""):
    s = s+v
    return s
s1=e("我")
s2=e("a","")
s3=e("是")
print(s1,s2,s3)
# 輸出: 我 a 是

其實以上型別都已經說明問題了,但是寫個文章不容易,我決定用元祖包列表,看看修改這個列表中的資料會怎樣。

實際上是不用測試的,最終列印出來的資料一定是類似**“可變型別時的操作”**時的輸出的。

為什麼?因為我沒有修改元祖本身,修改的是其可變型別列表啊。

不能扯遠了,不然扯到深拷貝,淺拷貝了。

測試:元祖包個列表來

def e(v,t=([],)):  # 傳遞有元素的元祖的時候要記得帶逗號哦。
    
    t[0].append(v)
    # t = t[0].append(v)     要知道t[0].append(v)是沒有返回值的,t會指向None,如果這樣返回,外部列印的全部為None,所以不可以這樣返回。
    # 而且 如果你想 t[0]= t[0].append(v) 也是不行的,為啥?你在ipython中輸入 dir(())你就知道了。
    # 好吧,其實是因為元祖是可讀不可寫的。它能切片、遍歷就已經很不錯了。。。。。
    return t
t1=e("我")
t2=e("a",([],))
t3=e("是")
print(t1,"n",t2,"n",t3)
# 輸出:
# (['我', '是'],) 
# (['a'],) 
# (['我', '是'],)

小結一下

家裡停電了,所以我來到了網咖,這篇文章是在網咖寫的,用的是python3的線上編輯器,有的地方編碼(比如命名-。-)或者表述的不好請多多見諒。

關於集合,我就不做測試了,集合一般用於去重和關係運算,它是無序,不重複,可變型別。 

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com