1. 程式人生 > 實用技巧 >python的預設引數的一個坑

python的預設引數的一個坑

前言

pass

正文

https://docs.python.org/3/tutorial/controlflow.html#default-argument-values 中,有這樣一段話

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:

大致意思為

重要警告:預設值僅計算一次。 當預設值是可變物件(例如列表,字典或大多數類的例項)時,這會有所不同。 例如,以下函式累積在後續呼叫中傳遞給它的引數:

下面給出了一個例子

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

打印出來的結果是

[1]
[1, 2]
[1, 2, 3]

這是因為, 作為預設引數, python在啟動時就會將變數 L 建立, 我們在函式 f 內對 L 處理就會導致後來呼叫的 L 已經是修改過的 L 了, 我們可以通過列印 L 的記憶體地址來驗證

def f(a, L=[]):
    print(id(L))
    L.append(a)
    print(id(L))
    return L

print(f(1))
print(f(2))
print(f(3))

結果是

4513289600
4513289600
[1]
4513289600
4513289600
[1, 2]
4513289600
4513289600
[1, 2, 3]

當然, python的文件中也給出了一個推薦的方法

If you don’t want the default to be shared between subsequent calls, you can write the function like this instead:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

即: 將 L 預設為 None, 當該引數未傳時, 將 L 設定為空列表, 在Python啟動時 L 會設定為 None, 而當我們呼叫而不傳引數 L 時, 在函式 f 內部重新給 L 賦值, 此時 L 為函式 f 內的一個私有變數, 其不會影響到引數 L 本身, 同樣的我們可以通過列印記憶體地址來驗證

def f(a, L=None):
    print(id(L))
    if L is None:
        L = []
    print(id(L))
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

輸出

4467395776
4469232960
[1]
4467395776
4469232960
[2]
4467395776
4469232960
[3]

當然我們也可以在不更改函式 f 的情況下, 在每次呼叫 f 的時候都傳輸引數 L, 如果想讓他為一個空列表那就傳一個空列表同樣能解決問題, 因為你每次指定了值, 便會在傳入時修改 L 的值

def f(a, L=[]):
    print(id(L))
    L.append(a)
    print(id(L))
    return L

print(f(1, []))
print(f(2, []))
print(f(3, []))

輸出

4474168192
4474168192
[1]
4474168192
4474168192
[2]
4474168192
4474168192
[3]