使用可選的可變引數定義函式

使用具有可變預設型別的可選引數時出現問題(在使用可選引數定義函式中進行了描述 ),這可能會導致意外行為。 ****

說明

這個問題是因為一個函式的預設引數初始化一旦被該功能時,在點定義,並不能 (像許多其他語言)當函式被稱為。預設值儲存在函式物件的 __defaults__ 成員變數中。

def f(a, b=42, c=[]):
    pass

print(f.__defaults__)
# Out: (42, [])

對於不可變型別(參見 Argument 傳遞和可變性 ),這不是問題,因為沒有辦法改變變數; 它只能被重新分配,保持原始值不變。因此,後續保證具有相同的預設值。但是,對於可變型別,通過呼叫其各種成員函式,原始值可以變異。因此,不保證對函式的連續呼叫具有初始預設值。

def append(elem, to=[]):
    to.append(elem)      # This call to append() mutates the default variable "to"
    return to

append(1)
# Out: [1]

append(2)  # Appends it to the internally stored list
# Out: [1, 2]

append(3, [])  # Using a new created list gives the expected result
# Out: [3]

# Calling it again without argument will append to the internally stored list again
append(4)   
# Out: [1, 2, 4]

注意: 某些 IDE(如 PyCharm)會在將可變型別指定為預設屬性時發出警告。

如果要確保預設引數始終是你在函式定義中指定的引數,那麼解決方案是始終使用不可變型別作為預設引數。

當需要使用可變型別作為預設值時,實現此目的的常用習慣是使用 None(不可變)作為預設引數,然後如果它等於 None 則將實際預設值分配給引數變數。

def append(elem, to=None):
    if to is None:
        to = []

    to.append(elem)
    return to