如果在調用一個函數時,沒有傳遞默認參數,則函數內的默認參數是對函數的默認參數屬性__defaults__的引用,
如
def func(arg1=[]): arg1.append(2)
調用func時如果沒有傳參,上面的arg1就是func.__defaults__[0]的引用
沒傳遞默認參數,會發生以下情況
由於func.__defaults__[0]是可變類型,導致每一次調用func,arg1都會對func.__defaults__[0]進行操作(func.__defaults__[0].append(2),
這樣在有些情況下會導致邏輯出錯的,例如
def func(arg1=[]): if(arg1==[]): print 'arg1 is empty' arg1.append(1) else: print 'arg1 is not empty' print arg1 func() # arg1 is empty
func() #arg1 is not empty [1]
第二次調用func的時候,並沒有傳遞參數arg1,但是第一次調用時,函數內部已經修改了__defaults__
這是為啥呢?為何第二次調用不重置arg1為[]?
因為
Python的默認參數只會在函數定義時被確定,而不是每次調用時重新確定,所以,一旦在函數中修改了默認參數,則再隨后的調用中都會生效
由於有這個特性,在定義函數時,如果默認參數使用可變的對象類型,如上例子,則很可能導致邏輯出錯,
所以,如不是特別需要,則不允許在函數內部對默認參數引用的func.__defaults__屬性進行修改,如何能讓一個對象不被修改?那就是在操作arg1前取消它對__defaults__的引用
以上例子改動一下
def func(arg1=[]): if(arg1==[]): print 'arg1 is empty' arg1=[] arg1.append(1) else: print 'arg1 is not empty' print arg1
上例中,在用戶沒有傳遞默認參數arg1時,函數內部會給arg1變量重新賦值,讓arg1去引用我們想用的對象[],這樣arg1就不會修改func.__defaults__了
如果是默認參數是有值的情況,可以這樣操作
def func(arg1=[1,2,3]): if(arg1==[1,2,3]): print 'is [1,2,3]' arg1=[1,2,3] #重點是這里,取消arg1對__defaults__屬性的引用,防止arg1修改__defaults__ arg1.append(1) else: print 'not [1,2,3]' print arg1
以上太啰嗦,下例模仿了python官方例子,使用不可變類型(例如None)作為默認參數,邏輯簡潔,推薦使用
def append_to(element, to=None): #默認參數to定義時為None,但函數邏輯中進行進一步重新賦值為想使用的默認值
if to is None: to = [1,2,3] to.append(element) return to
總結:
防止默認參數修改函數的__defaults__,需要:
1.定義默認參數時,最好使用不可變類型.
2.如果默認參數一定要使用可變類型,那就在函數內部對默認參數重新賦值為可變類型的具體值.