1. 構建有默認參數的函數
當我們在構建一個函數或者方法時,如果想使函數中的一個或者多個參數使可選的,並且有一個默認值,那么可以在函數定義中給參數指定一個默認值,並且放到參數列表的最后就行了。比如:
def func(a, b=42):
print(a, b)
func(1) #a=1, b=42
func(1,2) #a=1, b=2
如果默認參數使一個可以修改的容器,比如一個列表,集合或者字典,可以使用None作為默認值。比如:
#使用列表list作為默認值
def func(a, b=None):
if b is None:
b = []
···
但是,如果你並不是想提供一個默認值,而僅僅知識想測試下某個默認參數是不是有值傳遞進來,可以這樣寫:
_no_value = object()
def func(a, b=_no_value):
if b is _no_value:
print("b沒有值")
else:
print(a, b)
func(1) # b沒有值
func(1, None) # 1 None
仔細通過測試可以發現,傳遞一個None值和不傳值兩種情況是有差別的。
2. 參數陷阱
2.1 默認參數的值
默認參數的值僅僅在函數定義的時候賦值一次,示例代碼如下:
x = 42
def func(a, b=x):
print(a, b)
func(1) #1 42
x = 23
func(1) #1 42
從上面例子中可以看出,當我們改變x的值的對默認參數值並沒有影響,這是因為在函數定定義的時候就已經確定了它的默認值了。
2.2 默認參數值的類型
默認參數值的類型應該是不可變對象,比如None,True,False,數字或字符串,而不能使用列表,字典等可變類型。
不要像下面這樣寫代碼:
def func(a, b=[]): #不能這么寫
···
如果這樣寫了,當默認值在某些地方被修改之后,程序就會出現問題。
這些被修改的程序會影響到下次調用這個參數時的默認值。比如:
def func(a, b=[]):
return b
x = func(1)
print(x) #[]
x.append("a")
x.append("b")
print(x) #['a', 'b']
y = func(1)
print(y) #['a', 'b']
可以看到,b的默認值從一個空list,變成了[“a”, “b”],這種不會是你想要的默認參數。
為了避免這種情況的發生,最好是將默認值設置為None,然后在函數里面檢查它。
但是在將默認值設置為None之后,測試None值時使用is操作符是很重要的,is是這種方法的關鍵點。
有人可能會這樣寫:
def func(a, b=None):
if not b:
b = []
···
這么寫有有一個問題,就是雖然None確實會被當成False,但是還有其他的對象(比如長度為0的字符串,列表,元組,字典等)都會被當作False。因此上面的代碼會誤將一些其他輸入也會當作沒有輸入。比如:
def func(a, b=None):
if not b:
b = []
return b
print(func(1)) # []
print(func(1, [])) # []
print(func(1, 0)) # []
print(func(1, "")) # []
從結果中就可以看到,b的值並沒有發生改變,這不是我們想要的結果。
2.3 測試某個可選參數
當一個函數需要測試某個可選參數是否被使用者傳遞進來。這個時候需要小心的是不能用某個默認值,比如None,0或者False值來測試用戶提供的值(因為這些值都是合法的值,是可能被用戶傳遞進來的)。
你可以創建一個獨一 無二的私有對象實例,就像上面的_no_value變量一樣。在函數里面,你可以通過檢查被傳遞參數值跟這個實例是否一樣來判斷。
這里的思路就是用戶不可能去傳遞這個_no_value實例作為輸出。因此這里通過檢查這個值就能確定某個參數是否被傳遞進來了。
